From 3843109a625d3bf8420a2a919805318a53c628eb Mon Sep 17 00:00:00 2001 From: Philip Schell Date: Mon, 9 Dec 2019 14:46:34 +0100 Subject: [PATCH] Add netcore --- CoordinateSharp/Celestial.Assistant.cs | 2365 ++++++++--------- CoordinateSharp/Celestial.LunarEclipseCalc.cs | 715 +++-- CoordinateSharp/Celestial.MeeusTables.cs | 119 +- CoordinateSharp/Celestial.MoonCalculations.cs | 1503 +++++------ CoordinateSharp/Celestial.SolarEclipseCalc.cs | 1690 +++++------- CoordinateSharp/Celestial.SunCalculations.cs | 656 +++-- CoordinateSharp/Celestial.cs | 1126 ++++---- CoordinateSharp/Coordinate.Assistant.cs | 451 ++-- CoordinateSharp/Coordinate.Cartesian.cs | 278 +- CoordinateSharp/Coordinate.ECEF.cs | 1069 ++++---- CoordinateSharp/Coordinate.EagerLoad.cs | 206 +- CoordinateSharp/Coordinate.Formatting.cs | 191 +- CoordinateSharp/Coordinate.MGRS.cs | 498 ++-- CoordinateSharp/Coordinate.Parser.cs | 1605 +++++------ CoordinateSharp/Coordinate.UTM.cs | 1059 ++++---- CoordinateSharp/Coordinate.cs | 262 +- CoordinateSharp/CoordinateSharp_Core.csproj | 12 + CoordinateSharp/Distance.cs | 792 +++--- CoordinateSharp/Eclipse/LunarData.cs | 151 +- CoordinateSharp/Eclipse/SolarData.cs | 153 +- CoordinateSharp/GeoFence.cs | 287 +- CoordinateSharp/Properties/AssemblyInfo.cs | 4 +- CoordinateSharp_Core.sln | 25 + 23 files changed, 6884 insertions(+), 8333 deletions(-) create mode 100644 CoordinateSharp/CoordinateSharp_Core.csproj create mode 100644 CoordinateSharp_Core.sln diff --git a/CoordinateSharp/Celestial.Assistant.cs b/CoordinateSharp/Celestial.Assistant.cs index f2cbfa4..b1e7493 100644 --- a/CoordinateSharp/Celestial.Assistant.cs +++ b/CoordinateSharp/Celestial.Assistant.cs @@ -2,1316 +2,1157 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; -namespace CoordinateSharp -{ - internal partial class MeeusTables - { - - /// - /// Returns Moon Periodic Value Er - /// - /// Moon's mean elongation - /// Sun's mean anomaly - /// Moon's mean anomaly - /// Moon's argument of latitude - /// Dynamic time - /// Er - public static double Moon_Periodic_Er(double D, double M, double N, double F, double T) - { - //Table 47A contains 60 lines to sum - double[] values = new double[] { D, M, N, F }; - double sum = 0; - for (int x = 0; x < 60; x++) - { - sum += Get_Table47A_Values(values, x, T, false); - } - return sum; - } - /// - /// Returns Moon Periodic Value El - /// - /// Moon's mean longitude - /// Moon's mean elongation - /// Sun's mean anomaly - /// Moon's mean anomaly - /// Moon's argument of latitude - /// Dynamic time - /// El - public static double Moon_Periodic_El(double L, double D, double M, double N, double F, double T) - { - //Table 47A contains 60 lines to sum - double[] values = new double[] { D, M, N, F }; - double sum = 0; - for (int x = 0; x < 60; x++) - { - sum += Get_Table47A_Values(values, x, T, true); - } +namespace CoordinateSharp { + internal partial class MeeusTables { - //Planetary adjustments - double A1 = 119.75 + 131.849 * T; - double A2 = 53.09 + 479264.290 * T; - - //Normalize 0-360 degree number - A1 %= 360; - if (A1 < 0) { A1 += 360; } - A2 %= 360; - if (A2 < 0) { A2 += 360; } - - //Convert DMF to radians - A1 = A1 * Math.PI / 180; - A2 = A2 * Math.PI / 180; - - //L TO RADIANS - L %= 360; - if (L < 0) { L += 360; } - - //Convert DMF to radians - L = L * Math.PI / 180; - - sum += 3958 * Math.Sin(A1); - sum += 1962 * Math.Sin(L - F); - sum += 318 * Math.Sin(A2); - - return sum; - } - /// - /// Returns Moon Periodic Value Eb - /// - /// Moon's mean longitude - /// Moon's mean elongation - /// Sun's mean anomaly - /// Moon's mean anomaly - /// Moon's argument of latitude - /// Dynamic time - /// Eb - public static double Moon_Periodic_Eb(double L, double D, double M, double N, double F, double T) - { - //Table 47B contains 60 lines to sum - double[] values = new double[] { D, M, N, F }; - double sum = 0; - for (int x = 0; x < 60; x++) - { - sum += Get_Table47B_Values(values, x, T); - } - - //Planetary adjustments - double A1 = 119.75 + 131.849 * T; - double A3 = 313.45 + 481266.484 * T; - - //Normalize 0-360 degree number - A1 %= 360; - if (A1 < 0) { A1 += 360; } - A3 %= 360; - if (A3 < 0) { A3 += 360; } - - //Convert DMF to radians - A1 = A1 * Math.PI / 180; - A3 = A3 * Math.PI / 180; - - //L TO RADIANS - L %= 360; - if (L < 0) { L += 360; } - - //Convert DMF to radians - L = L * Math.PI / 180; - - sum += -2235 * Math.Sin(L); - sum += 382 * Math.Sin(A3); - sum += 175 * Math.Sin(A1 - F); - sum += 175 * Math.Sin(A1 + F); - sum += 127 * Math.Sin(L - M); - sum += -115 * Math.Sin(L + M); - - return sum; - } - //Ch 50 - /// - /// Sum of Apogee Terms from Jean Meeus Astronomical Algorithms Table 50.A - /// - /// Moom's mean elongation at time JDE - /// Sun's mean anomaly - /// Moon's arguement f latitude - /// Time in Julian centuries since epoch 2000 - /// double - public static double ApogeeTermsA(double D, double M, double F, double T) - { - double sum; - - sum = Math.Sin(2 * D) * 0.4392; - sum += Math.Sin(4 * D) * 0.0684; - sum += Math.Sin(M) * .0456 - 0.00011 * T; - sum += Math.Sin(2 * D - M) * .0426 - 0.00011 * T; - sum += Math.Sin(2 * F) * .0212; - sum += Math.Sin(D) * -0.0189; - sum += Math.Sin(6 * D) * .0144; - sum += Math.Sin(4 * D - M) * .0113; - sum += Math.Sin(2 * D + 2 * F) * .0047; - sum += Math.Sin(D + M) * .0036; - sum += Math.Sin(8 * D) * .0035; - sum += Math.Sin(6 * D - M) * .0034; - sum += Math.Sin(2 * D - 2 * F) * -0.0034; - sum += Math.Sin(2 * D - 2 * M) * .0022; - sum += Math.Sin(3 * D) * -.0017; - sum += Math.Sin(4 * D + 2 * F) * 0.0013; - - sum += Math.Sin(8 * D - M) * .0011; - sum += Math.Sin(4 * D - 2 * M) * .0010; - sum += Math.Sin(10 * D) * .0009; - sum += Math.Sin(3 * D + M) * .0007; - sum += Math.Sin(2 * M) * .0006; - sum += Math.Sin(2 * D + M) * .0005; - sum += Math.Sin(2 * D + 2 * M) * .0005; - sum += Math.Sin(6 * D + 2 * F) * .0004; - sum += Math.Sin(6 * D - 2 * M) * .0004; - sum += Math.Sin(10 * D - M) * .0004; - sum += Math.Sin(5 * D) * -0.0004; - sum += Math.Sin(4 * D - 2 * F) * -0.0004; - sum += Math.Sin(2 * F + M) * .0003; - sum += Math.Sin(12 * D) * .0003; - sum += Math.Sin(2 * D + 2 * F - M) * 0.0003; - sum += Math.Sin(D - M) * -0.0003; - return sum; - } - /// - /// Sum of Perigee Terms from Jean Meeus Astronomical Algorithms Table 50.A - /// - /// Moom's mean elongation at time JDE - /// Sun's mean anomaly - /// Moon's arguement f latitude - /// Time in Julian centuries since epoch 2000 - /// double - public static double PerigeeTermsA(double D, double M, double F, double T) - { - double sum; - - sum = Math.Sin(2 * D) * -1.6769; - sum += Math.Sin(4 * D) * .4589; - sum += Math.Sin(6 * D) * -.1856; - sum += Math.Sin(8 * D) * .0883; - sum += Math.Sin(2 * D - M) * -.0773 + .00019 * T; - sum += Math.Sin(M) * .0502 - .00013 * T; - sum += Math.Sin(10 * D) * -.0460; - sum += Math.Sin(4 * D - M) * .0422 - .00011 * T; - sum += Math.Sin(6 * D - M) * -.0256; - sum += Math.Sin(12 * D) * .0253; - sum += Math.Sin(D) * .0237; - sum += Math.Sin(8 * D - M) * .0162; - sum += Math.Sin(14 * D) * -.0145; - sum += Math.Sin(2 * F) * .0129; - sum += Math.Sin(3 * D) * -.0112; - sum += Math.Sin(10 * D - M) * -.0104; - sum += Math.Sin(16 * D) * .0086; - sum += Math.Sin(12 * D - M) * .0069; - sum += Math.Sin(5 * D) * .0066; - sum += Math.Sin(2 * D + 2 * F) * -.0053; - sum += Math.Sin(18 * D) * -.0052; - sum += Math.Sin(14 * D - M) * -.0046; - sum += Math.Sin(7 * D) * -.0041; - sum += Math.Sin(2 * D + M) * .0040; - sum += Math.Sin(20 * D) * .0032; - sum += Math.Sin(D + M) * -.0032; - sum += Math.Sin(16 * D - M) * .0031; - sum += Math.Sin(4 * D + M) * -.0029; - sum += Math.Sin(9 * D) * .0027; - sum += Math.Sin(4 * D + 2 * F) * .0027; - - sum += Math.Sin(2 * D - 2 * M) * -.0027; - sum += Math.Sin(4 * D - 2 * M) * .0024; - sum += Math.Sin(6 * D - 2 * M) * -.0021; - sum += Math.Sin(22 * D) * -.0021; - sum += Math.Sin(18 * D - M) * -.0021; - sum += Math.Sin(6 * D + M) * .0019; - sum += Math.Sin(11 * D) * -.0018; - sum += Math.Sin(8 * D + M) * -.0014; - sum += Math.Sin(4 * D - 2 * F) * -.0014; - sum += Math.Sin(6 * D + 2 * F) * -.0014; - sum += Math.Sin(3 * D + M) * .0014; - sum += Math.Sin(5 * D + M) * -.0014; - sum += Math.Sin(13 * D) * .0013; - sum += Math.Sin(20 * D - M) * .0013; - sum += Math.Sin(3 * D + 2 * M) * .0011; - sum += Math.Sin(4 * D + 2 * F - 2 * M) * -.0011; - sum += Math.Sin(D + 2 * M) * -.0010; - sum += Math.Sin(22 * D - M) * -.0009; - sum += Math.Sin(4 * F) * -.0008; - sum += Math.Sin(6 * D - 2 * F) * .0008; - sum += Math.Sin(2 * D - 2 * F + M) * .0008; - sum += Math.Sin(2 * M) * .0007; - sum += Math.Sin(2 * F - M) * .0007; - sum += Math.Sin(2 * D + 4 * F) * .0007; - sum += Math.Sin(2 * F - 2 * M) * -.0006; - sum += Math.Sin(2 * D - 2 * F + 2 * M) * -.0006; - sum += Math.Sin(24 * D) * .0006; - sum += Math.Sin(4 * D - 4 * F) * .0005; - sum += Math.Sin(2 * D + 2 * M) * .0005; - sum += Math.Sin(D - M) * -.0004; - - return sum; - } - /// - /// Sum of Apogee Terms from Jean Meeus Astronomical Algorithms Table 50.B - /// - /// Moom's mean elongation at time JDE - /// Sun's mean anomaly - /// Moon's arguement f latitude - /// Time in Julian centuries since epoch 2000 - /// double - public static double ApogeeTermsB(double D, double M, double F, double T) - { - double sum = 3245.251; - - sum += Math.Cos(2 * D) * -9.147; - sum += Math.Cos(D) * -.841; - sum += Math.Cos(2 * F) * .697; - sum += Math.Cos(M) * -0.656 + .0016 * T; - sum += Math.Cos(4 * D) * .355; - sum += Math.Cos(2 * D - M) * .159; - sum += Math.Cos(D + M) * .127; - sum += Math.Cos(4 * D - M) * .065; - - sum += Math.Cos(6 * D) * .052; - sum += Math.Cos(2 * D + M) * .043; - sum += Math.Cos(2 * D + 2 * F) * .031; - sum += Math.Cos(2 * D - 2 * F) * -.023; - sum += Math.Cos(2 * D - 2 * M) * .022; - sum += Math.Cos(2 * D + 2 * M) * .019; - sum += Math.Cos(2 * M) * -.016; - sum += Math.Cos(6 * D - M) * .014; - sum += Math.Cos(8 * D) * .010; - - return sum; - } - /// - /// Sum of Perigee Terms from Jean Meeus Astronomical Algorithms Table 50.B - /// - /// Moom's mean elongation at time JDE - /// Sun's mean anomaly - /// Moon's arguement f latitude - /// Time in Julian centuries since epoch 2000 - /// double - public static double PerigeeTermsB(double D, double M, double F, double T) - { - //Sum of Perigee Terms from Jean Meeus Astronomical Algorithms Table 50.B - double sum = 3629.215; - - sum += Math.Cos(2 * D) * 63.224; - sum += Math.Cos(4 * D) * -6.990; - sum += Math.Cos(2 * D - M) * 2.834 - .0071 * T; - sum += Math.Cos(6 * D) * 1.927; - sum += Math.Cos(D) * -1.263; - sum += Math.Cos(8 * D) * -.702; - sum += Math.Cos(M) * .696 - .0017 * T; - sum += Math.Cos(2 * F) * -.690; - sum += Math.Cos(4 * D - M) * -.629 + .0016 * T; - sum += Math.Cos(2 * D - 2 * F) * -.392; - sum += Math.Cos(10 * D) * .297; - sum += Math.Cos(6 * D - M) * .260; - sum += Math.Cos(3 * D) * .201; - sum += Math.Cos(2 * D + M) * -.161; - sum += Math.Cos(D + M) * .157; - sum += Math.Cos(12 * D) * -.138; - sum += Math.Cos(8 * D - M) * -.127; - sum += Math.Cos(2 * D + 2 * F) * .104; - sum += Math.Cos(2 * D - 2 * M) * .104; - sum += Math.Cos(5 * D) * -.079; - sum += Math.Cos(14 * D) * .068; - - sum += Math.Cos(10 * D - M) * .067; - sum += Math.Cos(4 * D + M) * .054; - sum += Math.Cos(12 * D - M) * -.038; - sum += Math.Cos(4 * D - 2 * M) * -.038; - sum += Math.Cos(7 * D) * .037; - sum += Math.Cos(4 * D + 2 * F) * -.037; - sum += Math.Cos(16 * D) * -.035; - sum += Math.Cos(3 * D + M) * -.030; - sum += Math.Cos(D - M) * .029; - sum += Math.Cos(6 * D + M) * -.025; - sum += Math.Cos(2 * M) * .023; - sum += Math.Cos(14 * D - M) * .023; - sum += Math.Cos(2 * D + 2 * M) * -.023; - sum += Math.Cos(6 * D - 2 * M) * .022; - sum += Math.Cos(2 * D - 2 * F - M) * -.021; - sum += Math.Cos(9 * D) * -.020; - sum += Math.Cos(18 * D) * .019; - sum += Math.Cos(6 * D + 2 * F) * .017; - sum += Math.Cos(2 * F - M) * .014; - sum += Math.Cos(16 * D - M) * -.014; - sum += Math.Cos(4 * D - 2 * F) * .013; - sum += Math.Cos(8 * D + M) * .012; - sum += Math.Cos(11 * D) * .011; - sum += Math.Cos(5 * D + M) * .010; - sum += Math.Cos(20 * D) * -.010; - - return sum; - } + /// + /// Returns Moon Periodic Value Er + /// + /// Moon's mean elongation + /// Sun's mean anomaly + /// Moon's mean anomaly + /// Moon's argument of latitude + /// Dynamic time + /// Er + public static Double Moon_Periodic_Er(Double D, Double M, Double N, Double F, Double T) { + //Table 47A contains 60 lines to sum + Double[] values = new Double[] { D, M, N, F }; + Double sum = 0; + for (Int32 x = 0; x < 60; x++) { + sum += Get_Table47A_Values(values, x, T, false); + } + return sum; } - internal class MeeusFormulas - { - public static double Get_Sidereal_Time(double JD) - { - //Ch. 12 - //T = Dynamic Time - //Oo = mean sidereal time at Greenwich at 0h UT - double T = (JD - 2451545) / 36525; - double Oo = 280.46061837 + 360.98564736629 * (JD - 2451545) + + /// + /// Returns Moon Periodic Value El + /// + /// Moon's mean longitude + /// Moon's mean elongation + /// Sun's mean anomaly + /// Moon's mean anomaly + /// Moon's argument of latitude + /// Dynamic time + /// El + public static Double Moon_Periodic_El(Double L, Double D, Double M, Double N, Double F, Double T) { + //Table 47A contains 60 lines to sum + Double[] values = new Double[] { D, M, N, F }; + Double sum = 0; + for (Int32 x = 0; x < 60; x++) { + sum += Get_Table47A_Values(values, x, T, true); + } + + //Planetary adjustments + Double A1 = 119.75 + 131.849 * T; + Double A2 = 53.09 + 479264.290 * T; + + //Normalize 0-360 degree number + A1 %= 360; + if (A1 < 0) { A1 += 360; } + A2 %= 360; + if (A2 < 0) { A2 += 360; } + + //Convert DMF to radians + A1 = A1 * Math.PI / 180; + A2 = A2 * Math.PI / 180; + + //L TO RADIANS + L %= 360; + if (L < 0) { L += 360; } + + //Convert DMF to radians + L = L * Math.PI / 180; + + sum += 3958 * Math.Sin(A1); + sum += 1962 * Math.Sin(L - F); + sum += 318 * Math.Sin(A2); + + return sum; + } + /// + /// Returns Moon Periodic Value Eb + /// + /// Moon's mean longitude + /// Moon's mean elongation + /// Sun's mean anomaly + /// Moon's mean anomaly + /// Moon's argument of latitude + /// Dynamic time + /// Eb + public static Double Moon_Periodic_Eb(Double L, Double D, Double M, Double N, Double F, Double T) { + //Table 47B contains 60 lines to sum + Double[] values = new Double[] { D, M, N, F }; + Double sum = 0; + for (Int32 x = 0; x < 60; x++) { + sum += Get_Table47B_Values(values, x, T); + } + + //Planetary adjustments + Double A1 = 119.75 + 131.849 * T; + Double A3 = 313.45 + 481266.484 * T; + + //Normalize 0-360 degree number + A1 %= 360; + if (A1 < 0) { A1 += 360; } + A3 %= 360; + if (A3 < 0) { A3 += 360; } + + //Convert DMF to radians + A1 = A1 * Math.PI / 180; + A3 = A3 * Math.PI / 180; + + //L TO RADIANS + L %= 360; + if (L < 0) { L += 360; } + + //Convert DMF to radians + L = L * Math.PI / 180; + + sum += -2235 * Math.Sin(L); + sum += 382 * Math.Sin(A3); + sum += 175 * Math.Sin(A1 - F); + sum += 175 * Math.Sin(A1 + F); + sum += 127 * Math.Sin(L - M); + sum += -115 * Math.Sin(L + M); + + return sum; + } + //Ch 50 + /// + /// Sum of Apogee Terms from Jean Meeus Astronomical Algorithms Table 50.A + /// + /// Moom's mean elongation at time JDE + /// Sun's mean anomaly + /// Moon's arguement f latitude + /// Time in Julian centuries since epoch 2000 + /// double + public static Double ApogeeTermsA(Double D, Double M, Double F, Double T) { + Double sum; + + sum = Math.Sin(2 * D) * 0.4392; + sum += Math.Sin(4 * D) * 0.0684; + sum += Math.Sin(M) * .0456 - 0.00011 * T; + sum += Math.Sin(2 * D - M) * .0426 - 0.00011 * T; + sum += Math.Sin(2 * F) * .0212; + sum += Math.Sin(D) * -0.0189; + sum += Math.Sin(6 * D) * .0144; + sum += Math.Sin(4 * D - M) * .0113; + sum += Math.Sin(2 * D + 2 * F) * .0047; + sum += Math.Sin(D + M) * .0036; + sum += Math.Sin(8 * D) * .0035; + sum += Math.Sin(6 * D - M) * .0034; + sum += Math.Sin(2 * D - 2 * F) * -0.0034; + sum += Math.Sin(2 * D - 2 * M) * .0022; + sum += Math.Sin(3 * D) * -.0017; + sum += Math.Sin(4 * D + 2 * F) * 0.0013; + + sum += Math.Sin(8 * D - M) * .0011; + sum += Math.Sin(4 * D - 2 * M) * .0010; + sum += Math.Sin(10 * D) * .0009; + sum += Math.Sin(3 * D + M) * .0007; + sum += Math.Sin(2 * M) * .0006; + sum += Math.Sin(2 * D + M) * .0005; + sum += Math.Sin(2 * D + 2 * M) * .0005; + sum += Math.Sin(6 * D + 2 * F) * .0004; + sum += Math.Sin(6 * D - 2 * M) * .0004; + sum += Math.Sin(10 * D - M) * .0004; + sum += Math.Sin(5 * D) * -0.0004; + sum += Math.Sin(4 * D - 2 * F) * -0.0004; + sum += Math.Sin(2 * F + M) * .0003; + sum += Math.Sin(12 * D) * .0003; + sum += Math.Sin(2 * D + 2 * F - M) * 0.0003; + sum += Math.Sin(D - M) * -0.0003; + return sum; + } + /// + /// Sum of Perigee Terms from Jean Meeus Astronomical Algorithms Table 50.A + /// + /// Moom's mean elongation at time JDE + /// Sun's mean anomaly + /// Moon's arguement f latitude + /// Time in Julian centuries since epoch 2000 + /// double + public static Double PerigeeTermsA(Double D, Double M, Double F, Double T) { + Double sum; + + sum = Math.Sin(2 * D) * -1.6769; + sum += Math.Sin(4 * D) * .4589; + sum += Math.Sin(6 * D) * -.1856; + sum += Math.Sin(8 * D) * .0883; + sum += Math.Sin(2 * D - M) * -.0773 + .00019 * T; + sum += Math.Sin(M) * .0502 - .00013 * T; + sum += Math.Sin(10 * D) * -.0460; + sum += Math.Sin(4 * D - M) * .0422 - .00011 * T; + sum += Math.Sin(6 * D - M) * -.0256; + sum += Math.Sin(12 * D) * .0253; + sum += Math.Sin(D) * .0237; + sum += Math.Sin(8 * D - M) * .0162; + sum += Math.Sin(14 * D) * -.0145; + sum += Math.Sin(2 * F) * .0129; + sum += Math.Sin(3 * D) * -.0112; + sum += Math.Sin(10 * D - M) * -.0104; + sum += Math.Sin(16 * D) * .0086; + sum += Math.Sin(12 * D - M) * .0069; + sum += Math.Sin(5 * D) * .0066; + sum += Math.Sin(2 * D + 2 * F) * -.0053; + sum += Math.Sin(18 * D) * -.0052; + sum += Math.Sin(14 * D - M) * -.0046; + sum += Math.Sin(7 * D) * -.0041; + sum += Math.Sin(2 * D + M) * .0040; + sum += Math.Sin(20 * D) * .0032; + sum += Math.Sin(D + M) * -.0032; + sum += Math.Sin(16 * D - M) * .0031; + sum += Math.Sin(4 * D + M) * -.0029; + sum += Math.Sin(9 * D) * .0027; + sum += Math.Sin(4 * D + 2 * F) * .0027; + + sum += Math.Sin(2 * D - 2 * M) * -.0027; + sum += Math.Sin(4 * D - 2 * M) * .0024; + sum += Math.Sin(6 * D - 2 * M) * -.0021; + sum += Math.Sin(22 * D) * -.0021; + sum += Math.Sin(18 * D - M) * -.0021; + sum += Math.Sin(6 * D + M) * .0019; + sum += Math.Sin(11 * D) * -.0018; + sum += Math.Sin(8 * D + M) * -.0014; + sum += Math.Sin(4 * D - 2 * F) * -.0014; + sum += Math.Sin(6 * D + 2 * F) * -.0014; + sum += Math.Sin(3 * D + M) * .0014; + sum += Math.Sin(5 * D + M) * -.0014; + sum += Math.Sin(13 * D) * .0013; + sum += Math.Sin(20 * D - M) * .0013; + sum += Math.Sin(3 * D + 2 * M) * .0011; + sum += Math.Sin(4 * D + 2 * F - 2 * M) * -.0011; + sum += Math.Sin(D + 2 * M) * -.0010; + sum += Math.Sin(22 * D - M) * -.0009; + sum += Math.Sin(4 * F) * -.0008; + sum += Math.Sin(6 * D - 2 * F) * .0008; + sum += Math.Sin(2 * D - 2 * F + M) * .0008; + sum += Math.Sin(2 * M) * .0007; + sum += Math.Sin(2 * F - M) * .0007; + sum += Math.Sin(2 * D + 4 * F) * .0007; + sum += Math.Sin(2 * F - 2 * M) * -.0006; + sum += Math.Sin(2 * D - 2 * F + 2 * M) * -.0006; + sum += Math.Sin(24 * D) * .0006; + sum += Math.Sin(4 * D - 4 * F) * .0005; + sum += Math.Sin(2 * D + 2 * M) * .0005; + sum += Math.Sin(D - M) * -.0004; + + return sum; + } + /// + /// Sum of Apogee Terms from Jean Meeus Astronomical Algorithms Table 50.B + /// + /// Moom's mean elongation at time JDE + /// Sun's mean anomaly + /// Moon's arguement f latitude + /// Time in Julian centuries since epoch 2000 + /// double + public static Double ApogeeTermsB(Double D, Double M, Double F, Double T) { + Double sum = 3245.251; + + sum += Math.Cos(2 * D) * -9.147; + sum += Math.Cos(D) * -.841; + sum += Math.Cos(2 * F) * .697; + sum += Math.Cos(M) * -0.656 + .0016 * T; + sum += Math.Cos(4 * D) * .355; + sum += Math.Cos(2 * D - M) * .159; + sum += Math.Cos(D + M) * .127; + sum += Math.Cos(4 * D - M) * .065; + + sum += Math.Cos(6 * D) * .052; + sum += Math.Cos(2 * D + M) * .043; + sum += Math.Cos(2 * D + 2 * F) * .031; + sum += Math.Cos(2 * D - 2 * F) * -.023; + sum += Math.Cos(2 * D - 2 * M) * .022; + sum += Math.Cos(2 * D + 2 * M) * .019; + sum += Math.Cos(2 * M) * -.016; + sum += Math.Cos(6 * D - M) * .014; + sum += Math.Cos(8 * D) * .010; + + return sum; + } + /// + /// Sum of Perigee Terms from Jean Meeus Astronomical Algorithms Table 50.B + /// + /// Moom's mean elongation at time JDE + /// Sun's mean anomaly + /// Moon's arguement f latitude + /// Time in Julian centuries since epoch 2000 + /// double + public static Double PerigeeTermsB(Double D, Double M, Double F, Double T) { + //Sum of Perigee Terms from Jean Meeus Astronomical Algorithms Table 50.B + Double sum = 3629.215; + + sum += Math.Cos(2 * D) * 63.224; + sum += Math.Cos(4 * D) * -6.990; + sum += Math.Cos(2 * D - M) * 2.834 - .0071 * T; + sum += Math.Cos(6 * D) * 1.927; + sum += Math.Cos(D) * -1.263; + sum += Math.Cos(8 * D) * -.702; + sum += Math.Cos(M) * .696 - .0017 * T; + sum += Math.Cos(2 * F) * -.690; + sum += Math.Cos(4 * D - M) * -.629 + .0016 * T; + sum += Math.Cos(2 * D - 2 * F) * -.392; + sum += Math.Cos(10 * D) * .297; + sum += Math.Cos(6 * D - M) * .260; + sum += Math.Cos(3 * D) * .201; + sum += Math.Cos(2 * D + M) * -.161; + sum += Math.Cos(D + M) * .157; + sum += Math.Cos(12 * D) * -.138; + sum += Math.Cos(8 * D - M) * -.127; + sum += Math.Cos(2 * D + 2 * F) * .104; + sum += Math.Cos(2 * D - 2 * M) * .104; + sum += Math.Cos(5 * D) * -.079; + sum += Math.Cos(14 * D) * .068; + + sum += Math.Cos(10 * D - M) * .067; + sum += Math.Cos(4 * D + M) * .054; + sum += Math.Cos(12 * D - M) * -.038; + sum += Math.Cos(4 * D - 2 * M) * -.038; + sum += Math.Cos(7 * D) * .037; + sum += Math.Cos(4 * D + 2 * F) * -.037; + sum += Math.Cos(16 * D) * -.035; + sum += Math.Cos(3 * D + M) * -.030; + sum += Math.Cos(D - M) * .029; + sum += Math.Cos(6 * D + M) * -.025; + sum += Math.Cos(2 * M) * .023; + sum += Math.Cos(14 * D - M) * .023; + sum += Math.Cos(2 * D + 2 * M) * -.023; + sum += Math.Cos(6 * D - 2 * M) * .022; + sum += Math.Cos(2 * D - 2 * F - M) * -.021; + sum += Math.Cos(9 * D) * -.020; + sum += Math.Cos(18 * D) * .019; + sum += Math.Cos(6 * D + 2 * F) * .017; + sum += Math.Cos(2 * F - M) * .014; + sum += Math.Cos(16 * D - M) * -.014; + sum += Math.Cos(4 * D - 2 * F) * .013; + sum += Math.Cos(8 * D + M) * .012; + sum += Math.Cos(11 * D) * .011; + sum += Math.Cos(5 * D + M) * .010; + sum += Math.Cos(20 * D) * -.010; + + return sum; + } + + } + internal class MeeusFormulas { + public static Double Get_Sidereal_Time(Double JD) { + //Ch. 12 + //T = Dynamic Time + //Oo = mean sidereal time at Greenwich at 0h UT + Double T = (JD - 2451545) / 36525; + Double Oo = 280.46061837 + 360.98564736629 * (JD - 2451545) + .000387933 * Math.Pow(T, 2) - Math.Pow(T, 3) / 38710000; - return Oo; - } + return Oo; } + } + /// + /// Used to display a celestial condition for a specified date. + /// + [Serializable] + public enum CelestialStatus { /// - /// Used to display a celestial condition for a specified date. + /// Celestial body rises and sets on the set day. /// - [Serializable] - public enum CelestialStatus - { - /// - /// Celestial body rises and sets on the set day. - /// - RiseAndSet, - /// - /// Celestial body is down all day - /// - DownAllDay, - /// - /// Celestial body is up all day - /// - UpAllDay, - /// - /// Celestial body rises, but does not set on the set day - /// - NoRise, - /// - /// Celestial body sets, but does not rise on the set day - /// - NoSet - } + RiseAndSet, /// - /// moon perigee or apogee indicator + /// Celestial body is down all day /// - internal enum MoonDistanceType - { - /// - /// Moon's perigee - /// - Perigee, - /// - /// Moon's apogee - /// - Apogee - } + DownAllDay, /// - /// Moon Illumination Information + /// Celestial body is up all day /// - [Serializable] - public class MoonIllum - { + UpAllDay, + /// + /// Celestial body rises, but does not set on the set day + /// + NoRise, + /// + /// Celestial body sets, but does not rise on the set day + /// + NoSet + } + /// + /// moon perigee or apogee indicator + /// + internal enum MoonDistanceType { + /// + /// Moon's perigee + /// + Perigee, + /// + /// Moon's apogee + /// + Apogee + } + /// + /// Moon Illumination Information + /// + [Serializable] + public class MoonIllum { - /// - /// Moon's fraction - /// - public double Fraction { get; internal set; } - /// - /// Moon's Angle - /// - public double Angle { get; internal set; } - /// - /// Moon's phase - /// - public double Phase { get; internal set; } - /// - /// Moon's phase name for the specified day - /// - public string PhaseName { get; internal set; } + /// + /// Moon's fraction + /// + public Double Fraction { get; internal set; } + /// + /// Moon's Angle + /// + public Double Angle { get; internal set; } + /// + /// Moon's phase + /// + public Double Phase { get; internal set; } + /// + /// Moon's phase name for the specified day + /// + public String PhaseName { get; internal set; } + + } + /// + /// Stores Perigee or Apogee values + /// + [Serializable] + public class PerigeeApogee { + + /// + /// Initializes a Perigee or Apogee object + /// + /// Date of Event + /// Horizontal Parallax + /// Distance + public PerigeeApogee(DateTime d, Double p, Distance dist) { + this.Date = d; + this.HorizontalParallax = p; + this.Distance = dist; + } + + /// + /// Date of event. + /// + public DateTime Date { get; } + /// + /// Horizontal Parallax. + /// + public Double HorizontalParallax { get; } + /// + /// Moon's distance at event. + /// + public Distance Distance { get; } + + internal void Convert_To_Local_Time(Double offset) { + FieldInfo[] fields = typeof(PerigeeApogee).GetFields(BindingFlags.NonPublic | BindingFlags.Instance); + foreach (FieldInfo field in fields) { + if (field.FieldType == typeof(DateTime)) { + DateTime d = (DateTime)field.GetValue(this); + if (d > new DateTime()) { + d = d.AddHours(offset); + field.SetValue(this, d); + } + } + } + } + + } + /// + /// Julian date conversions + /// + public class JulianConversions { + //1.1.3.1 + private static readonly Double J1970 = 2440588, J2000 = 2451545; + + /// + /// Returns JD. + /// Meeus Ch 7. + /// + /// DateTime + /// JDE + public static Double GetJulian(DateTime d) { + Double y = d.Year; + Double m = d.Month; + Double dy = d.Day + d.TimeOfDay.TotalHours / 24; + + //If month is Jan or Feb add 12 to month and reduce year by 1. + if (m <= 2) { m += 12; y -= 1; } + + Double A = (Int32)(d.Year / 100.0); + Double B = 0; + + //Gregorian Start Date + if (d >= new DateTime(1582, 10, 15)) { + B = 2 - A + (Int32)(A / 4.0); + } + Double JD = (Int32)(365.25 * (y + 4716)) + (Int32)(30.6001 * (m + 1)) + dy + B - 1524.5; + return JD; + } + /// + /// Returns JD from epoch 2000. + /// Meeus Ch 7. + /// + /// DateTime + /// JDE + public static Double GetJulian_Epoch2000(DateTime d) => GetJulian(d) - J2000; + /// + /// Returns JD from epoch 1970. + /// Meeus Ch 7. + /// + /// DateTime + /// JDE + public static Double GetJulian_Epoch1970(DateTime d) => GetJulian(d) - J1970; + + /// + /// Returns date from Julian + /// Meeus ch. 7 + /// + /// Julian + /// DateTime + public static DateTime? GetDate_FromJulian(Double j) { + if (Double.IsNaN(j)) { return null; } //No Event Occured + + j += .5; + Double Z = Math.Floor(j); + Double F = j - Z; + Double A = Z; + if (Z >= 2299161) { + Double a = (Int32)((Z - 1867216.25) / 36524.25); + A = Z + 1 + a - (Int32)(a / 4.0); + } + Double B = A + 1524; + Double C = (Int32)((B - 122.1) / 365.25); + Double D = (Int32)(365.25 * C); + Double E = (Int32)((B - D) / 30.6001); + + Double day = B - D - (Int32)(30.6001 * E) + F; + + //Month is E-1 if month is < 14 or E-13 if month is 14 or 15 + Double month = E - 1; + if (E > 13) { month -= 12; } + + //year is C-4716 if month>2 and C-4715 if month is 1 or 2 + Double year = C - 4715; + if (month > 2) { + year -= 1; + } + + Double hours = day - Math.Floor(day); + hours *= 24; + Double minutes = hours - Math.Floor(hours); + minutes *= 60; + Double seconds = minutes - Math.Floor(minutes); + seconds *= 60; + + day = Math.Floor(day); + hours = Math.Floor(hours); + minutes = Math.Floor(minutes); + + DateTime? date = new DateTime?(new DateTime((Int32)year, (Int32)month, (Int32)day, (Int32)hours, (Int32)minutes, (Int32)seconds)); + return date; + } + /// + /// Returns date from Julian based on epoch 2000 + /// Meeus ch. 7 + /// + /// Julian + /// DateTime + public static DateTime? GetDate_FromJulian_Epoch2000(Double j) => GetDate_FromJulian(j + J2000); + /// + /// Returns date from Julian based on epoch 1970 + /// Meeus ch. 7 + /// + /// Julian + /// DateTime + public static DateTime? GetDate_FromJulian_Epoch1970(Double j) => GetDate_FromJulian(j + J1970); + } + /// + /// Contains last and next perigee + /// + [Serializable] + public class Perigee { + + /// + /// Initializes an Perigee object. + /// + /// + /// + public Perigee(PerigeeApogee last, PerigeeApogee next) { + this.LastPerigee = last; + this.NextPerigee = next; + } + + /// + /// Last perigee + /// + public PerigeeApogee LastPerigee { get; } + /// + /// Next perigee + /// + public PerigeeApogee NextPerigee { get; } + + internal void ConvertTo_Local_Time(Double offset) { + this.LastPerigee.Convert_To_Local_Time(offset); + this.NextPerigee.Convert_To_Local_Time(offset); + } + + } + /// + /// Contains last and next apogee + /// + [Serializable] + public class Apogee { + + /// + /// Initializes an Apogee object. + /// + /// + /// + public Apogee(PerigeeApogee last, PerigeeApogee next) { + this.LastApogee = last; + this.NextApogee = next; + } + + /// + /// Last apogee + /// + public PerigeeApogee LastApogee { get; } + /// + /// Next apogee + /// + public PerigeeApogee NextApogee { get; } + + internal void ConvertTo_Local_Time(Double offset) { + this.LastApogee.Convert_To_Local_Time(offset); + this.NextApogee.Convert_To_Local_Time(offset); + } + } + /// + /// Astrological Signs + /// + [Serializable] + public class AstrologicalSigns { + /// + /// Astrological Zodiac Sign + /// + public String MoonName { get; internal set; } + /// + /// Astrological Moon Sign + /// + public String MoonSign { get; internal set; } + /// + /// Astrological Zodiac Sign + /// + public String ZodiacSign { get; internal set; } + } + /// + /// Additional Solar Time Information + /// + [Serializable] + public class AdditionalSolarTimes { + /// + /// Create an AdditionalSolarTimes object. + /// + public AdditionalSolarTimes() { + //Set dates to avoid null errors. If year return 1900 event did not occur. + this.CivilDawn = new DateTime(); + this.CivilDusk = new DateTime(); + this.NauticalDawn = new DateTime(); + this.NauticalDusk = new DateTime(); } /// - /// Stores Perigee or Apogee values + /// Returns Civil Dawn Time /// - [Serializable] - public class PerigeeApogee - { - private DateTime date; - private double horizontalParallax; - private Distance distance; + public DateTime? CivilDawn { get; internal set; } + /// + /// Returns Civil Dusk Time + /// + public DateTime? CivilDusk { get; internal set; } + /// + /// Returns Nautical Dawn Time + /// + public DateTime? NauticalDawn { get; internal set; } + /// + /// Returns Nautical Dusk Time + /// + public DateTime? NauticalDusk { get; internal set; } + /// + /// Returns Astronomical Dawn Time + /// + public DateTime? AstronomicalDawn { get; internal set; } + /// + /// Returns Astronomical Dusk Time + /// + public DateTime? AstronomicalDusk { get; internal set; } - /// - /// Initializes a Perigee or Apogee object - /// - /// Date of Event - /// Horizontal Parallax - /// Distance - public PerigeeApogee(DateTime d, double p, Distance dist) - { - date = d; - horizontalParallax = p; - distance = dist; - } + /// + /// Returns the time when the bottom of the solar disc touches the horizon after sunrise + /// + public DateTime? SunriseBottomDisc { get; internal set; } + /// + /// Returns the time when the bottom of the solar disc touches the horizon before sunset + /// + public DateTime? SunsetBottomDisc { get; internal set; } - /// - /// Date of event. - /// - public DateTime Date - { - get { return date; } - } - /// - /// Horizontal Parallax. - /// - public double HorizontalParallax - { - get { return horizontalParallax; } - } - /// - /// Moon's distance at event. - /// - public Distance Distance - { - get { return distance; } - } - - internal void Convert_To_Local_Time(double offset) - { - FieldInfo[] fields = typeof(PerigeeApogee).GetFields(BindingFlags.NonPublic | BindingFlags.Instance); - foreach (FieldInfo field in fields) - { - if (field.FieldType == typeof(DateTime)) - { - DateTime d = (DateTime)field.GetValue(this); - if (d > new DateTime()) - { - d = d.AddHours(offset); - field.SetValue(this, d); - } - } + internal void Convert_To_Local_Time(Double offset) { + FieldInfo[] fields = typeof(AdditionalSolarTimes).GetFields(BindingFlags.NonPublic | BindingFlags.Instance); + foreach (FieldInfo field in fields) { + if (field.FieldType == typeof(DateTime?)) { + DateTime? d = (DateTime?)field.GetValue(this); + if (d.HasValue) { + if (d > new DateTime()) { + d = d.Value.AddHours(offset); + field.SetValue(this, d); } + } } - + } + } + } + /// + /// Class containing solar eclipse information + /// + [Serializable] + public class SolarEclipse { + /// + /// Initialize a SolarEclipse object + /// + public SolarEclipse() { + this.LastEclipse = new SolarEclipseDetails(); + this.NextEclipse = new SolarEclipseDetails(); } /// - /// Julian date conversions + /// Details about the previous solar eclipse /// - public class JulianConversions - { - //1.1.3.1 - private static double J1970 = 2440588, J2000 = 2451545; + public SolarEclipseDetails LastEclipse { get; internal set; } + /// + /// Details about the next solar eclipse + /// + public SolarEclipseDetails NextEclipse { get; internal set; } - /// - /// Returns JD. - /// Meeus Ch 7. - /// - /// DateTime - /// JDE - public static double GetJulian(DateTime d) - { - double y = d.Year; - double m = d.Month; - double dy = d.Day + (d.TimeOfDay.TotalHours / 24); - - //If month is Jan or Feb add 12 to month and reduce year by 1. - if(m <= 2) { m += 12; y -= 1; } - - double A = (int)(d.Year / 100.0); - double B = 0; - - //Gregorian Start Date - if(d >= new DateTime(1582,10,15)) - { - B = 2 - A + (int)(A / 4.0); - } - double JD = (int)(365.25 * (y + 4716)) + (int)(30.6001 * (m + 1)) + dy + B - 1524.5; - return JD; - } - /// - /// Returns JD from epoch 2000. - /// Meeus Ch 7. - /// - /// DateTime - /// JDE - public static double GetJulian_Epoch2000(DateTime d) - { - return GetJulian(d) - J2000; - } - /// - /// Returns JD from epoch 1970. - /// Meeus Ch 7. - /// - /// DateTime - /// JDE - public static double GetJulian_Epoch1970(DateTime d) - { - return GetJulian(d) - J1970; - } - - /// - /// Returns date from Julian - /// Meeus ch. 7 - /// - /// Julian - /// DateTime - public static DateTime? GetDate_FromJulian(double j) - { - if (Double.IsNaN(j)) { return null; } //No Event Occured - - j = j + .5; - double Z = Math.Floor(j); - double F = j - Z; - double A = Z; - if (Z >= 2299161) - { - double a = (int)((Z - 1867216.25) / 36524.25); - A = Z + 1 + a - (int)(a / 4.0); - } - double B = A + 1524; - double C = (int)((B - 122.1) / 365.25); - double D = (int)(365.25 * C); - double E = (int)((B - D) / 30.6001); - - double day = B - D - (int)(30.6001 * E) + F; - - //Month is E-1 if month is < 14 or E-13 if month is 14 or 15 - double month = E-1; - if (E > 13) { month -= 12; } - - //year is C-4716 if month>2 and C-4715 if month is 1 or 2 - double year = C - 4715; - if(month >2) - { - year -= 1; - } - - double hours = day - Math.Floor(day); - hours *= 24; - double minutes = hours - Math.Floor(hours); - minutes *= 60; - double seconds = minutes - Math.Floor(minutes); - seconds *= 60; - - day = Math.Floor(day); - hours = Math.Floor(hours); - minutes = Math.Floor(minutes); - - DateTime? date = new DateTime?(new DateTime((int)year, (int)month, (int)day, (int)hours, (int)minutes, (int)seconds)); - return date; - } - /// - /// Returns date from Julian based on epoch 2000 - /// Meeus ch. 7 - /// - /// Julian - /// DateTime - public static DateTime? GetDate_FromJulian_Epoch2000(double j) - { - return GetDate_FromJulian(j+J2000); - } - /// - /// Returns date from Julian based on epoch 1970 - /// Meeus ch. 7 - /// - /// Julian - /// DateTime - public static DateTime? GetDate_FromJulian_Epoch1970(double j) - { - return GetDate_FromJulian(j + J1970); - } + internal void ConvertTo_LocalTime(Double offset) { + this.LastEclipse.Convert_To_Local_Time(offset); + this.NextEclipse.Convert_To_Local_Time(offset); + } + } + /// + /// Class containing lunar eclipse information + /// + [Serializable] + public class LunarEclipse { + /// + /// Initialize a LunarEclipse object + /// + public LunarEclipse() { + this.LastEclipse = new LunarEclipseDetails(); + this.NextEclipse = new LunarEclipseDetails(); } /// - /// Contains last and next perigee + /// Details about the previous lunar eclipse /// - [Serializable] - public class Perigee - { - private PerigeeApogee lastPerigee; - private PerigeeApogee nextPerigee; + public LunarEclipseDetails LastEclipse { get; internal set; } + /// + /// Details about the next lunar eclipse + /// + public LunarEclipseDetails NextEclipse { get; internal set; } - /// - /// Initializes an Perigee object. - /// - /// - /// - public Perigee(PerigeeApogee last, PerigeeApogee next) - { - lastPerigee = last; - nextPerigee = next; + internal void ConvertTo_LocalTime(Double offset) { + this.LastEclipse.Convert_To_Local_Time(offset); + this.NextEclipse.Convert_To_Local_Time(offset); + } + } + /// + /// Class containing specific solar eclipse information + /// + [Serializable] + public class SolarEclipseDetails { + + /// + /// Initialize a SolarEclipseDetails object + /// + /// Solar Eclipse String Values + public SolarEclipseDetails(List values) { + //Eclipse has value + this.HasEclipseData = true; + //Set Eclipse Date + this.Date = Convert.ToDateTime(values[0]); + + switch (values[1]) { + case "P": + this.Type = SolarEclipseType.Partial; + break; + case "A": + this.Type = SolarEclipseType.Annular; + break; + case "T": + this.Type = SolarEclipseType.Total; + break; + default: + break; + } + //Eclipse start + if (TimeSpan.TryParse(values[2], out TimeSpan ts)) { + this.PartialEclispeBegin = this.Date.Add(ts); + } + //A or T start + if (TimeSpan.TryParse(values[4], out ts)) { + this.AorTEclipseBegin = this.Date.Add(ts); + } + //Maximum Eclipse + if (TimeSpan.TryParse(values[5], out ts)) { + this.MaximumEclipse = this.Date.Add(ts); + } + //A or T ends + if (TimeSpan.TryParse(values[8], out ts)) { + this.AorTEclipseEnd = this.Date.Add(ts); + } + //Eclipse end + if (TimeSpan.TryParse(values[9], out ts)) { + this.PartialEclispeEnd = this.Date.Add(ts); + } + //A or T Duration + if (values[13] != "-") { + String s = values[13].Replace("m", ":").Replace("s", ""); + String[] ns = s.Split(':'); + Int32 secs = 0; + + _ = Int32.TryParse(ns[0], out Int32 mins); + if (ns.Count() > 0) { + _ = Int32.TryParse(ns[1], out secs); } - /// - /// Last perigee - /// - public PerigeeApogee LastPerigee { get { return lastPerigee; } } - /// - /// Next perigee - /// - public PerigeeApogee NextPerigee { get { return nextPerigee; } } - - internal void ConvertTo_Local_Time(double offset) - { - LastPerigee.Convert_To_Local_Time(offset); - NextPerigee.Convert_To_Local_Time(offset); - } + TimeSpan time = new TimeSpan(0, mins, secs); + this.AorTDuration = time; + } else { + this.AorTDuration = new TimeSpan(); + } + this.Adjust_Dates();//Adjust dates if required (needed when eclipse crosses into next day). } /// - /// Contains last and next apogee + /// Initialize an empty SolarEclipseDetails object /// - [Serializable] - public class Apogee - { - private PerigeeApogee lastApogee; - private PerigeeApogee nextApogee; + public SolarEclipseDetails() => this.HasEclipseData = false; + /// + /// JS Eclipse Calc formulas didn't account for Z time calculation. + /// Iterate through and adjust Z dates where eclipse is passed midnight. + /// + private void Adjust_Dates() { + //Load array in reverse event order + DateTime[] dateArray = new DateTime[] { this.PartialEclispeBegin, this.AorTEclipseBegin, this.MaximumEclipse, this.AorTEclipseEnd, this.PartialEclispeEnd }; + DateTime baseTime = this.PartialEclispeEnd; + Boolean multiDay = false; //used to detrmine if eclipse crossed into next Z day - /// - /// Initializes an Apogee object. - /// - /// - /// - public Apogee(PerigeeApogee last, PerigeeApogee next) - { - lastApogee = last; - nextApogee = next; - } - - /// - /// Last apogee - /// - public PerigeeApogee LastApogee { get { return lastApogee; } } - /// - /// Next apogee - /// - public PerigeeApogee NextApogee { get { return nextApogee; } } - - internal void ConvertTo_Local_Time(double offset) - { - LastApogee.Convert_To_Local_Time(offset); - NextApogee.Convert_To_Local_Time(offset); + for (Int32 x = 4; x >= 0; x--) { + DateTime d = dateArray[x]; + //Check if date exist + if (d > new DateTime()) { + + //Adjust if time is less than then baseTime. + if (d > baseTime) { + switch (x) { + case 3: + this.AorTEclipseEnd = this.AorTEclipseEnd.AddDays(-1); + break; + case 2: + this.MaximumEclipse = this.MaximumEclipse.AddDays(-1); + break; + case 1: + this.AorTEclipseBegin = this.AorTEclipseBegin.AddDays(-1); + break; + case 0: + this.PartialEclispeBegin = this.PartialEclispeBegin.AddDays(-1); + break; + default: + break; + } + + multiDay = true;//Set true to change base date value. + } } + } + if (multiDay) { + this.Date = this.Date.AddDays(-1); //Shave day off base date if multiday. + } } /// - /// Astrological Signs + /// Determine if the SolarEclipseDetails object has been populated /// - [Serializable] - public class AstrologicalSigns - { - /// - /// Astrological Zodiac Sign - /// - public string MoonName { get; internal set; } - /// - /// Astrological Moon Sign - /// - public string MoonSign { get; internal set; } - /// - /// Astrological Zodiac Sign - /// - public string ZodiacSign { get; internal set; } - } + public Boolean HasEclipseData { get; } /// - /// Additional Solar Time Information + /// Date of solar eclipse /// - [Serializable] - public class AdditionalSolarTimes - { - /// - /// Create an AdditionalSolarTimes object. - /// - public AdditionalSolarTimes() - { - //Set dates to avoid null errors. If year return 1900 event did not occur. - CivilDawn = new DateTime(); - CivilDusk = new DateTime(); - NauticalDawn = new DateTime(); - NauticalDusk = new DateTime(); - - } - /// - /// Returns Civil Dawn Time - /// - public DateTime? CivilDawn { get; internal set; } - /// - /// Returns Civil Dusk Time - /// - public DateTime? CivilDusk { get; internal set; } - /// - /// Returns Nautical Dawn Time - /// - public DateTime? NauticalDawn { get; internal set; } - /// - /// Returns Nautical Dusk Time - /// - public DateTime? NauticalDusk { get; internal set; } - /// - /// Returns Astronomical Dawn Time - /// - public DateTime? AstronomicalDawn { get; internal set; } - /// - /// Returns Astronomical Dusk Time - /// - public DateTime? AstronomicalDusk { get; internal set; } - - /// - /// Returns the time when the bottom of the solar disc touches the horizon after sunrise - /// - public DateTime? SunriseBottomDisc{ get; internal set; } - /// - /// Returns the time when the bottom of the solar disc touches the horizon before sunset - /// - public DateTime? SunsetBottomDisc { get; internal set; } - - internal void Convert_To_Local_Time(double offset) - { - FieldInfo[] fields = typeof(AdditionalSolarTimes).GetFields(BindingFlags.NonPublic | BindingFlags.Instance); - foreach (FieldInfo field in fields) - { - if (field.FieldType == typeof(DateTime?)) - { - DateTime? d = (DateTime?)field.GetValue(this); - if (d.HasValue) - { - if (d > new DateTime()) - { - d = d.Value.AddHours(offset); - field.SetValue(this, d); - } - } - } - } - } - } - /// - /// Class containing solar eclipse information - /// - [Serializable] - public class SolarEclipse - { - /// - /// Initialize a SolarEclipse object - /// - public SolarEclipse() - { - LastEclipse = new SolarEclipseDetails(); - NextEclipse = new SolarEclipseDetails(); - } - /// - /// Details about the previous solar eclipse - /// - public SolarEclipseDetails LastEclipse { get; internal set; } - /// - /// Details about the next solar eclipse - /// - public SolarEclipseDetails NextEclipse { get; internal set; } - - internal void ConvertTo_LocalTime(double offset) - { - LastEclipse.Convert_To_Local_Time(offset); - NextEclipse.Convert_To_Local_Time(offset); - } - } - /// - /// Class containing lunar eclipse information - /// - [Serializable] - public class LunarEclipse - { - /// - /// Initialize a LunarEclipse object - /// - public LunarEclipse() - { - LastEclipse = new LunarEclipseDetails(); - NextEclipse = new LunarEclipseDetails(); - } - /// - /// Details about the previous lunar eclipse - /// - public LunarEclipseDetails LastEclipse { get; internal set; } - /// - /// Details about the next lunar eclipse - /// - public LunarEclipseDetails NextEclipse { get; internal set; } - - internal void ConvertTo_LocalTime(double offset) - { - LastEclipse.Convert_To_Local_Time(offset); - NextEclipse.Convert_To_Local_Time(offset); - } - } - /// - /// Class containing specific solar eclipse information - /// - [Serializable] - public class SolarEclipseDetails - { - private DateTime date; - private SolarEclipseType type; - private DateTime partialEclispeBegin; - private DateTime aorTEclipseBegin; - private DateTime maximumEclipse; - private DateTime aorTEclipseEnd; - private DateTime partialEclispeEnd; - private TimeSpan aorTDuration; - private bool hasEclipseData; - - /// - /// Initialize a SolarEclipseDetails object - /// - /// Solar Eclipse String Values - public SolarEclipseDetails(List values) - { - //Eclipse has value - hasEclipseData = true; - //Set Eclipse Date - date = Convert.ToDateTime(values[0]); - - switch (values[1]) - { - case "P": - type = SolarEclipseType.Partial; - break; - case "A": - type = SolarEclipseType.Annular; - break; - case "T": - type = SolarEclipseType.Total; - break; - default: - break; - } - TimeSpan ts; - //Eclipse start - if (TimeSpan.TryParse(values[2], out ts)) - { - partialEclispeBegin = date.Add(ts); - } - //A or T start - if (TimeSpan.TryParse(values[4], out ts)) - { - aorTEclipseBegin = date.Add(ts); - } - //Maximum Eclipse - if (TimeSpan.TryParse(values[5], out ts)) - { - maximumEclipse = date.Add(ts); - } - //A or T ends - if (TimeSpan.TryParse(values[8], out ts)) - { - aorTEclipseEnd = date.Add(ts); - } - //Eclipse end - if (TimeSpan.TryParse(values[9], out ts)) - { - partialEclispeEnd = date.Add(ts); - } - //A or T Duration - if (values[13] != "-") - { - string s = values[13].Replace("m", ":").Replace("s", ""); - string[] ns = s.Split(':'); - int mins = 0; - int secs = 0; - - int.TryParse(ns[0], out mins); - if (ns.Count() > 0) - { - int.TryParse(ns[1], out secs); - } - - TimeSpan time = new TimeSpan(0, mins, secs); - - aorTDuration = time; - } - else - { - aorTDuration = new TimeSpan(); - } - Adjust_Dates();//Adjust dates if required (needed when eclipse crosses into next day). - } - /// - /// Initialize an empty SolarEclipseDetails object - /// - public SolarEclipseDetails() - { - hasEclipseData = false; - } - /// - /// JS Eclipse Calc formulas didn't account for Z time calculation. - /// Iterate through and adjust Z dates where eclipse is passed midnight. - /// - private void Adjust_Dates() - { - //Load array in reverse event order - DateTime[] dateArray = new DateTime[] { partialEclispeBegin, aorTEclipseBegin, maximumEclipse, aorTEclipseEnd, partialEclispeEnd }; - DateTime baseTime = partialEclispeEnd; - bool multiDay = false; //used to detrmine if eclipse crossed into next Z day - - for (int x = 4; x >= 0; x--) - { - DateTime d = dateArray[x]; - //Check if date exist - if (d > new DateTime()) - { - - //Adjust if time is less than then baseTime. - if (d > baseTime) - { - switch (x) - { - case 3: - aorTEclipseEnd = aorTEclipseEnd.AddDays(-1); - break; - case 2: - maximumEclipse = maximumEclipse.AddDays(-1); - break; - case 1: - aorTEclipseBegin = aorTEclipseBegin.AddDays(-1); - break; - case 0: - partialEclispeBegin = partialEclispeBegin.AddDays(-1); - break; - default: - break; - } - - multiDay = true;//Set true to change base date value. - } - } - } - if (multiDay) - { - this.date = this.date.AddDays(-1); //Shave day off base date if multiday. - } - } - /// - /// Determine if the SolarEclipseDetails object has been populated - /// - public bool HasEclipseData { get { return hasEclipseData; } } - /// - /// Date of solar eclipse - /// - public DateTime Date { get { return date; } } - /// - /// Solar eclipse type - /// - public SolarEclipseType Type { get { return type; } } - /// - /// DateTime when the partial eclipse begins - /// - public DateTime PartialEclispeBegin { get { return partialEclispeBegin; } } - /// - /// DateTime when an Annular or Total eclipse begins (if applicable) - /// - public DateTime AorTEclipseBegin { get { return aorTEclipseBegin; } } - /// - /// DateTime when eclipse is at Maximum - /// - public DateTime MaximumEclipse { get { return maximumEclipse; } } - - /// - /// DateTime when the Annular or Total eclipse ends (if applicable) - /// - public DateTime AorTEclipseEnd { get { return aorTEclipseEnd; } } - /// - /// DateTime when the partial elipse ends - /// - public DateTime PartialEclispeEnd { get { return partialEclispeEnd; } } - /// - /// Duration of Annular or Total eclipse (if applicable) - /// - public TimeSpan AorTDuration { get { return aorTDuration; } } - /// - /// Solat eclipse default string - /// - /// Solar eclipse base date string - public override string ToString() - { - return date.ToString("dd-MMM-yyyy"); - } - - internal void Convert_To_Local_Time(double offset) - { - FieldInfo[] fields = typeof(SolarEclipseDetails).GetFields(BindingFlags.NonPublic | BindingFlags.Instance); - foreach (FieldInfo field in fields) - { - if (field.FieldType == typeof(DateTime)) - { - DateTime d = (DateTime)field.GetValue(this); - if (d > new DateTime()) - { - d = d.AddHours(offset); - field.SetValue(this, d); - } - } - } - - date = partialEclispeBegin.Date; - } - } - /// - /// Class containing specific lunar eclipse information - /// - [Serializable] - public class LunarEclipseDetails - { - private DateTime date; - private LunarEclipseType type; - private DateTime penumbralEclipseBegin; - private DateTime partialEclispeBegin; - private DateTime totalEclipseBegin; - private DateTime midEclipse; - private DateTime totalEclipseEnd; - private DateTime partialEclispeEnd; - private DateTime penumbralEclipseEnd; - - private bool hasEclipseData; - - /// - /// Initialize a LunarEclipseDetails object - /// - /// Lunar Eclipse String Values - public LunarEclipseDetails(List values) - { - //Eclipse has value - hasEclipseData = true; - //Set Eclipse Date - date = Convert.ToDateTime(values[0]); - switch (values[1]) - { - case "T": - type = LunarEclipseType.Total; - break; - case "P": - type = LunarEclipseType.Partial; - break; - case "N": - type = LunarEclipseType.Penumbral; - break; - default: - break; - } - TimeSpan ts; - //Penumbral Eclipse start - if (TimeSpan.TryParse(values[4], out ts)) - { - penumbralEclipseBegin = date.Add(ts); - } - //PartialEclipse start - if (TimeSpan.TryParse(values[6], out ts)) - { - partialEclispeBegin = date.Add(ts); - } - //Total start - if (TimeSpan.TryParse(values[8], out ts)) - { - totalEclipseBegin = date.Add(ts); - } - //Mid Eclipse - if (TimeSpan.TryParse(values[10], out ts)) - { - midEclipse = date.Add(ts); - } - //Total ends - if (TimeSpan.TryParse(values[12], out ts)) - { - totalEclipseEnd = date.Add(ts); - } - //Partial Eclipse end - if (TimeSpan.TryParse(values[14], out ts)) - { - partialEclispeEnd = date.Add(ts); - } - //Penumbral Eclipse end - if (TimeSpan.TryParse(values[16], out ts)) - { - penumbralEclipseEnd = date.Add(ts); - } - Adjust_Dates(); - } - /// - /// Initialize an empty LunarEclipseDetails object - /// - public LunarEclipseDetails() - { - hasEclipseData = false; - } - /// - /// JS Eclipse Calc formulas didn't account for Z time calculation. - /// Iterate through and adjust Z dates where eclipse is passed midnight. - /// - private void Adjust_Dates() - { - //Load array in squential order. - DateTime[] dateArray = new DateTime[] { penumbralEclipseBegin, partialEclispeBegin, totalEclipseBegin, midEclipse, totalEclipseEnd, partialEclispeEnd, penumbralEclipseEnd }; - DateTime baseTime = partialEclispeEnd; - bool multiDay = false; //used to detrmine if eclipse crossed into next Z day - baseTime = penumbralEclipseBegin; - for (int x = 0; x < dateArray.Count(); x++) - { - DateTime d = dateArray[x]; - //Check if date exist - if (d > new DateTime()) - { - if (d < baseTime) - { - multiDay = true; - } - } - baseTime = dateArray[x]; - if (multiDay == true) - { - switch (x) - { - case 1: - partialEclispeBegin = partialEclispeBegin.AddDays(1); - break; - case 2: - totalEclipseBegin = totalEclipseBegin.AddDays(1); - break; - case 3: - midEclipse = midEclipse.AddDays(1); - break; - case 4: - totalEclipseEnd = totalEclipseEnd.AddDays(1); - break; - case 5: - partialEclispeEnd = partialEclispeEnd.AddDays(1); - break; - case 6: - penumbralEclipseEnd = penumbralEclipseEnd.AddDays(1); - break; - default: - break; - } - } - } - } - - /// - /// Determine if the LunarEclipseDetails object has been populated - /// - public bool HasEclipseData { get { return hasEclipseData; } } - /// - /// Date of lunar eclipse - /// - public DateTime Date { get { return date; } } - /// - /// Lunar eclipse type - /// - public LunarEclipseType Type { get { return type; } } - /// - /// DateTime when the penumbral eclipse begins - /// - public DateTime PenumbralEclipseBegin { get { return penumbralEclipseBegin; } } - /// - /// DateTime when the partial eclipse begins (if applicable) - /// - public DateTime PartialEclispeBegin { get { return partialEclispeBegin; } } - /// - /// DateTime when Total eclipse begins (if applicable) - /// - public DateTime TotalEclipseBegin { get { return totalEclipseBegin; } } - /// - /// DateTime when eclipse is at Mid - /// - public DateTime MidEclipse { get { return midEclipse; } } - /// - /// DateTime when Total eclipse ends (if applicable) - /// - public DateTime TotalEclipseEnd { get { return totalEclipseEnd; } } - /// - /// DateTime when the partial elipse ends (if applicable) - /// - public DateTime PartialEclispeEnd { get { return partialEclispeEnd; } } - /// - /// DateTime when the penumbral elipse ends - /// - public DateTime PenumbralEclispeEnd { get { return penumbralEclipseEnd; } } - /// - /// Lunar eclipse default string - /// - /// Lunar eclipse base date string - public override string ToString() - { - return date.ToString("dd-MMM-yyyy"); - } - - internal void Convert_To_Local_Time(double offset) - { - FieldInfo[] fields = typeof(LunarEclipseDetails).GetFields(BindingFlags.NonPublic |BindingFlags.Instance); - foreach (FieldInfo field in fields) - { - if (field.FieldType == typeof(DateTime)) - { - DateTime d = (DateTime)field.GetValue(this); - if (d > new DateTime()) - { - d = d.AddHours(offset); - field.SetValue(this, d); - } - } - } - date = penumbralEclipseBegin.Date; - - } - - } - internal class MoonTimes - { - public DateTime set { get; internal set; } - public DateTime rise { get; internal set; } - public CelestialStatus status { get; internal set; } - } - internal class MoonPosition - { - public double Azimuth { get; internal set; } - public double Altitude { get; internal set; } - public Distance Distance { get; internal set; } - public double ParallacticAngle { get; internal set; } - public double ParallaxCorection { get; internal set; } - } - internal class CelCoords - { - public double ra { get; internal set; } - public double dec { get; internal set; } - public double dist { get; internal set; } - } - + public DateTime Date { get; private set; } /// /// Solar eclipse type /// - [Serializable] - public enum SolarEclipseType - { - /// - /// Partial Eclipse - /// - Partial, - /// - /// Annular Eclipse - /// - Annular, - /// - /// Total Eclipse...of the heart... - /// - Total + public SolarEclipseType Type { get; } + /// + /// DateTime when the partial eclipse begins + /// + public DateTime PartialEclispeBegin { get; private set; } + /// + /// DateTime when an Annular or Total eclipse begins (if applicable) + /// + public DateTime AorTEclipseBegin { get; private set; } + /// + /// DateTime when eclipse is at Maximum + /// + public DateTime MaximumEclipse { get; private set; } + + /// + /// DateTime when the Annular or Total eclipse ends (if applicable) + /// + public DateTime AorTEclipseEnd { get; private set; } + /// + /// DateTime when the partial elipse ends + /// + public DateTime PartialEclispeEnd { get; } + /// + /// Duration of Annular or Total eclipse (if applicable) + /// + public TimeSpan AorTDuration { get; } + /// + /// Solat eclipse default string + /// + /// Solar eclipse base date string + public override String ToString() => this.Date.ToString("dd-MMM-yyyy"); + + internal void Convert_To_Local_Time(Double offset) { + FieldInfo[] fields = typeof(SolarEclipseDetails).GetFields(BindingFlags.NonPublic | BindingFlags.Instance); + foreach (FieldInfo field in fields) { + if (field.FieldType == typeof(DateTime)) { + DateTime d = (DateTime)field.GetValue(this); + if (d > new DateTime()) { + d = d.AddHours(offset); + field.SetValue(this, d); + } + } + } + + this.Date = this.PartialEclispeBegin.Date; } + } + /// + /// Class containing specific lunar eclipse information + /// + [Serializable] + public class LunarEclipseDetails { + + /// + /// Initialize a LunarEclipseDetails object + /// + /// Lunar Eclipse String Values + public LunarEclipseDetails(List values) { + //Eclipse has value + this.HasEclipseData = true; + //Set Eclipse Date + this.Date = Convert.ToDateTime(values[0]); + switch (values[1]) { + case "T": + this.Type = LunarEclipseType.Total; + break; + case "P": + this.Type = LunarEclipseType.Partial; + break; + case "N": + this.Type = LunarEclipseType.Penumbral; + break; + default: + break; + } + //Penumbral Eclipse start + if (TimeSpan.TryParse(values[4], out TimeSpan ts)) { + this.PenumbralEclipseBegin = this.Date.Add(ts); + } + //PartialEclipse start + if (TimeSpan.TryParse(values[6], out ts)) { + this.PartialEclispeBegin = this.Date.Add(ts); + } + //Total start + if (TimeSpan.TryParse(values[8], out ts)) { + this.TotalEclipseBegin = this.Date.Add(ts); + } + //Mid Eclipse + if (TimeSpan.TryParse(values[10], out ts)) { + this.MidEclipse = this.Date.Add(ts); + } + //Total ends + if (TimeSpan.TryParse(values[12], out ts)) { + this.TotalEclipseEnd = this.Date.Add(ts); + } + //Partial Eclipse end + if (TimeSpan.TryParse(values[14], out ts)) { + this.PartialEclispeEnd = this.Date.Add(ts); + } + //Penumbral Eclipse end + if (TimeSpan.TryParse(values[16], out ts)) { + this.PenumbralEclispeEnd = this.Date.Add(ts); + } + this.Adjust_Dates(); + } + /// + /// Initialize an empty LunarEclipseDetails object + /// + public LunarEclipseDetails() => this.HasEclipseData = false; + /// + /// JS Eclipse Calc formulas didn't account for Z time calculation. + /// Iterate through and adjust Z dates where eclipse is passed midnight. + /// + private void Adjust_Dates() { + //Load array in squential order. + DateTime[] dateArray = new DateTime[] { this.PenumbralEclipseBegin, this.PartialEclispeBegin, this.TotalEclipseBegin, this.MidEclipse, this.TotalEclipseEnd, this.PartialEclispeEnd, this.PenumbralEclispeEnd }; + Boolean multiDay = false; //used to detrmine if eclipse crossed into next Z day + DateTime baseTime = this.PenumbralEclipseBegin; + for (Int32 x = 0; x < dateArray.Count(); x++) { + DateTime d = dateArray[x]; + //Check if date exist + if (d > new DateTime()) { + if (d < baseTime) { + multiDay = true; + } + } + baseTime = dateArray[x]; + if (multiDay == true) { + switch (x) { + case 1: + this.PartialEclispeBegin = this.PartialEclispeBegin.AddDays(1); + break; + case 2: + this.TotalEclipseBegin = this.TotalEclipseBegin.AddDays(1); + break; + case 3: + this.MidEclipse = this.MidEclipse.AddDays(1); + break; + case 4: + this.TotalEclipseEnd = this.TotalEclipseEnd.AddDays(1); + break; + case 5: + this.PartialEclispeEnd = this.PartialEclispeEnd.AddDays(1); + break; + case 6: + this.PenumbralEclispeEnd = this.PenumbralEclispeEnd.AddDays(1); + break; + default: + break; + } + } + } + } + + /// + /// Determine if the LunarEclipseDetails object has been populated + /// + public Boolean HasEclipseData { get; } + /// + /// Date of lunar eclipse + /// + public DateTime Date { get; private set; } /// /// Lunar eclipse type /// - [Serializable] - public enum LunarEclipseType - { - /// - /// Penumbral Eclipse - /// - Penumbral, - /// - /// Partial Eclipse - /// - Partial, - /// - /// Total Eclipse...of the heart... - /// - Total + public LunarEclipseType Type { get; } + /// + /// DateTime when the penumbral eclipse begins + /// + public DateTime PenumbralEclipseBegin { get; } + /// + /// DateTime when the partial eclipse begins (if applicable) + /// + public DateTime PartialEclispeBegin { get; private set; } + /// + /// DateTime when Total eclipse begins (if applicable) + /// + public DateTime TotalEclipseBegin { get; private set; } + /// + /// DateTime when eclipse is at Mid + /// + public DateTime MidEclipse { get; private set; } + /// + /// DateTime when Total eclipse ends (if applicable) + /// + public DateTime TotalEclipseEnd { get; private set; } + /// + /// DateTime when the partial elipse ends (if applicable) + /// + public DateTime PartialEclispeEnd { get; private set; } + /// + /// DateTime when the penumbral elipse ends + /// + public DateTime PenumbralEclispeEnd { get; private set; } + /// + /// Lunar eclipse default string + /// + /// Lunar eclipse base date string + public override String ToString() => this.Date.ToString("dd-MMM-yyyy"); + + internal void Convert_To_Local_Time(Double offset) { + FieldInfo[] fields = typeof(LunarEclipseDetails).GetFields(BindingFlags.NonPublic | BindingFlags.Instance); + foreach (FieldInfo field in fields) { + if (field.FieldType == typeof(DateTime)) { + DateTime d = (DateTime)field.GetValue(this); + if (d > new DateTime()) { + d = d.AddHours(offset); + field.SetValue(this, d); + } + } + } + this.Date = this.PenumbralEclipseBegin.Date; + } + + } + internal class MoonTimes { + public DateTime Set { get; internal set; } + public DateTime Rise { get; internal set; } + public CelestialStatus Status { get; internal set; } + } + internal class MoonPosition { + public Double Azimuth { get; internal set; } + public Double Altitude { get; internal set; } + public Distance Distance { get; internal set; } + public Double ParallacticAngle { get; internal set; } + public Double ParallaxCorection { get; internal set; } + } + internal class CelCoords { + public Double Ra { get; internal set; } + public Double Dec { get; internal set; } + public Double Dist { get; internal set; } + } + + /// + /// Solar eclipse type + /// + [Serializable] + public enum SolarEclipseType { + /// + /// Partial Eclipse + /// + Partial, + /// + /// Annular Eclipse + /// + Annular, + /// + /// Total Eclipse...of the heart... + /// + Total + } + /// + /// Lunar eclipse type + /// + [Serializable] + public enum LunarEclipseType { + /// + /// Penumbral Eclipse + /// + Penumbral, + /// + /// Partial Eclipse + /// + Partial, + /// + /// Total Eclipse...of the heart... + /// + Total + } } diff --git a/CoordinateSharp/Celestial.LunarEclipseCalc.cs b/CoordinateSharp/Celestial.LunarEclipseCalc.cs index 80951c9..a89d10f 100644 --- a/CoordinateSharp/Celestial.LunarEclipseCalc.cs +++ b/CoordinateSharp/Celestial.LunarEclipseCalc.cs @@ -1,412 +1,321 @@ using System; using System.Collections.Generic; -namespace CoordinateSharp -{ - //CURRENT ALTITUDE IS SET CONSTANT AT 100M. POSSIBLY NEED TO ADJUST TO ALLOW USER PASS. - //Altitude adjustments appear to have minimal effect on eclipse timing. These were mainly used - //to signify eclipses that had already started during rise and set times on the NASA calculator +namespace CoordinateSharp { + //CURRENT ALTITUDE IS SET CONSTANT AT 100M. POSSIBLY NEED TO ADJUST TO ALLOW USER PASS. + //Altitude adjustments appear to have minimal effect on eclipse timing. These were mainly used + //to signify eclipses that had already started during rise and set times on the NASA calculator - //SOME TIMES AND ALTS WERE RETURNED WITH COLOR AND STYLING. DETERMINE WHY AND ADJUST VALUE AS REQUIRED. SEARCH "WAS ITALIC". + //SOME TIMES AND ALTS WERE RETURNED WITH COLOR AND STYLING. DETERMINE WHY AND ADJUST VALUE AS REQUIRED. SEARCH "WAS ITALIC". - //ELLIPSOID ADJUSTMENT - //6378140.0 Ellipsoid is used in the NASA Calculator - //WGS84 Ellipsoid is 6378137.0. Adjustments to the ellipsoid appear to effect eclipse seconds in fractions. - //This can be modified if need to allow users to pass custom number with the Coordinate SetDatum() functions. + //ELLIPSOID ADJUSTMENT + //6378140.0 Ellipsoid is used in the NASA Calculator + //WGS84 Ellipsoid is 6378137.0. Adjustments to the ellipsoid appear to effect eclipse seconds in fractions. + //This can be modified if need to allow users to pass custom number with the Coordinate SetDatum() functions. - //CURRENT RANGE 1601-2600. - internal class LunarEclipseCalc - { - public static List> CalculateLunarEclipse(DateTime d, double latRad, double longRad) - { - return Calculate(d, latRad, longRad); - } - public static List CalculateLunarEclipse(DateTime d, double latRad, double longRad, double[] events) - { - List> evs = Calculate(d, latRad, longRad, events); - List deetsList = new List(); - foreach (List ls in evs) - { - LunarEclipseDetails deets = new LunarEclipseDetails(ls); - deetsList.Add(deets); - } - return deetsList; - } - public static List> CalculateLunarEclipse(DateTime d, Coordinate coord) - { - return Calculate(d, coord.Latitude.ToRadians(), coord.Longitude.ToRadians()); - } - - - // CALCULATE! - private static List> Calculate(DateTime d, double latRad, double longRad, double[] ev = null) - { - //DECLARE ARRAYS - double[] obsvconst = new double[6]; - double[] mid = new double[41]; - double[] p1 = new double[41]; - double[] u1 = new double[41]; - double[] u2 = new double[41]; - double[] u3 = new double[41]; - double[] u4 = new double[41]; - double[] p4 = new double[41]; - - List> events = new List>(); - - double[] el; - if (ev == null) - { - el = Eclipse.LunarData.LunarDateData(d);//Get 100 year solar data; - } - else - { - el = ev; - } - - events = new List>(); - ReadData(latRad, longRad, obsvconst); - - for (int i = 0; i < el.Length; i += 22) - { - if (el[5 + i] <= obsvconst[5]) - { - List values = new List(); - obsvconst[4] = i; - GetAll(el, obsvconst, mid, p1, u1, u2,u3,u4,p4); - // Is there an event... - if (mid[5] != 1) - { - - values.Add(GetDate(el, p1, obsvconst)); - - if (el[5 + i] == 1) - { - values.Add("T"); - } - else if (el[5 + i] == 2) - { - values.Add("P"); - } - else - { - values.Add("N"); - } - - // Pen. Mag - values.Add(el[3 + i].ToString()); - - // Umbral Mag - values.Add(el[4 + i].ToString()); - - // P1 - values.Add(GetTime(el, p1, obsvconst)); - - // P1 alt - values.Add(GetAlt(p1)); - - if (u1[5] == 1) - { - values.Add("-"); - values.Add("-"); - } - else - { - // U1 - values.Add(GetTime(el, u1, obsvconst)); - - // U1 alt - values.Add(GetAlt(u1)); - } - if (u2[5] == 1) - { - values.Add("-"); - values.Add("-"); - } - else - { - // U2 - values.Add(GetTime(el, u2, obsvconst)); - - // U2 alt - values.Add(GetAlt(u2)); - } - // mid - - values.Add(GetTime(el, mid, obsvconst)); - - // mid alt - - values.Add(GetAlt(mid)); - - if (u3[5] == 1) - { - values.Add("-"); - values.Add("-"); - } - else - { - // u3 - values.Add(GetTime(el, u3, obsvconst)); - - // u3 alt - values.Add(GetAlt(u3)); - } - if (u4[5] == 1) - { - values.Add("-"); - values.Add("-"); - } - else - { - // u4 - values.Add(GetTime(el, u4, obsvconst)); - - // u4 alt - values.Add(GetAlt(u4)); - - } - // P4 - values.Add(GetTime(el, p4, obsvconst)); - - // P4 alt - values.Add(GetAlt(p4)); - events.Add(values); - } - } - } - return events; - } - // Read the data that's in the form, and populate the obsvconst array - private static void ReadData(double latRad, double longRad, double[] obsvconst) - { - - // Get the latitude - obsvconst[0] = latRad; - - // Get the longitude - obsvconst[1] = -1 * longRad; //PASS REVERSE RADIAN. - - // Get the altitude - obsvconst[2] = 100; //CHANGE TO ALLOW USER TO PASS. - - // Get the time zone - obsvconst[3] = 0; //GMT TIME - - obsvconst[4] = 0; //INDEX - - //SET MAX ECLIPSE TYPE - obsvconst[5] = 4;//4 is ALL Eclipses - - } - // Populate the p1, u1, u2, mid, u3, u4 and p4 arrays - private static void GetAll(double[] elements, double[] obsvconst, double[] mid, double[] p1, double[] u1, double[] u2, double[] u3, double[] u4, double[] p4) - { - int index = (int)obsvconst[4]; - p1[1] = elements[index + 9]; - PopulateCircumstances(elements, p1, obsvconst); - mid[1] = elements[index + 12]; - PopulateCircumstances(elements, mid, obsvconst); - p4[1] = elements[index + 15]; - PopulateCircumstances(elements, p4, obsvconst); - if (elements[index + 5] < 3) - { - u1[1] = elements[index + 10]; - PopulateCircumstances(elements, u1, obsvconst); - u4[1] = elements[index + 14]; - PopulateCircumstances(elements, u4, obsvconst); - if (elements[index + 5] < 2) - { - u2[1] = elements[index + 11]; - u3[1] = elements[index + 13]; - PopulateCircumstances(elements, u2, obsvconst); - PopulateCircumstances(elements, u3, obsvconst); - } - else - { - u2[5] = 1; - u3[5] = 1; - } - } - else - { - u1[5] = 1; - u2[5] = 1; - u3[5] = 1; - u4[5] = 1; - } - if ((p1[5] != 0) && (u1[5] != 0) && (u2[5] != 0) && (mid[5] != 0) && (u3[5] != 0) && (u4[5] != 0) && (p4[5] != 0)) - { - mid[5] = 1; - } - } - // Populate the circumstances array - // entry condition - circumstances[1] must contain the correct value - private static void PopulateCircumstances(double[] elements, double[] circumstances, double[] obsvconst) - { - double t, ra, dec, h; - - int index = (int)obsvconst[4]; - t = circumstances[1]; - ra = elements[18 + index] * t + elements[17 + index]; - ra = ra * t + elements[16 + index]; - dec = elements[21 + index] * t + elements[20 + index]; - dec = dec * t + elements[19 + index]; - dec = dec * Math.PI / 180.0; - circumstances[3] = dec; - h = 15.0 * (elements[6 + index] + (t - elements[2 + index] / 3600.0) * 1.00273791) - ra; - h = h * Math.PI / 180.0 - obsvconst[1]; - circumstances[2] = h; - circumstances[4] = Math.Asin(Math.Sin(obsvconst[0]) * Math.Sin(dec) + Math.Cos(obsvconst[0]) * Math.Cos(dec) * Math.Cos(h)); - circumstances[4] -= Math.Asin(Math.Sin(elements[7 + index] * Math.PI / 180.0) * Math.Cos(circumstances[4])); - if (circumstances[4] * 180.0 / Math.PI < elements[8 + index] - 0.5667) - { - circumstances[5] = 2; - } - else if (circumstances[4] < 0.0) - { - circumstances[4] = 0.0; - circumstances[5] = 0; - } - else - { - circumstances[5] = 0; - } - } - // Get the date of an event - private static string GetDate(double[] elements, double[] circumstances, double[] obsvconst) - { - string[] month = new string[] { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };//Month string array - double t, jd, a, b, c, d, e; - string ans = ""; - int index = (int)obsvconst[4]; - // Calculate the JD for noon (TDT) the day before the day that contains T0 - jd = Math.Floor(elements[index] - (elements[1 + index] / 24.0)); - // Calculate the local time (ie the offset in hours since midnight TDT on the day containing T0). - t = circumstances[1] + elements[1 + index] - obsvconst[3] - (elements[2 + index] - 30.0) / 3600.0; - - if (t < 0.0) - { - jd--; - } - if (t >= 24.0) - { - jd++; - } - if (jd >= 2299160.0) - { - a = Math.Floor((jd - 1867216.25) / 36524.25); - a = jd + 1 + a - Math.Floor(a / 4.0); - } - else - { - a = jd; - } - b = a + 1525.0; - c = Math.Floor((b - 122.1) / 365.25); - d = Math.Floor(365.25 * c); - e = Math.Floor((b - d) / 30.6001); - d = b - d - Math.Floor(30.6001 * e); - if (e < 13.5) - { - e = e - 1; - } - else - { - e = e - 13; - } - double year; - if (e > 2.5) - { - ans = c - 4716 + "-"; - year = c - 4716; - } - else - { - ans = c - 4715 + "-"; - year = c - 4715; - } - string m = month[(int)e - 1]; - ans += m+ "-"; - if (d < 10) - { - ans = ans + "0"; - } - ans = ans + d; - //Leap Year Integrity Check - - if (m == "Feb" && d == 29 && !DateTime.IsLeapYear((int)year)) - { - ans = year.ToString() + "-Mar-01"; - } - return ans; - } - // Get the time of an event - private static string GetTime(double[] elements, double[] circumstances, double[] obsvconst) - { - double t; - string ans = ""; - - int index = (int)obsvconst[4]; - t = circumstances[1] + elements[1 + index] - obsvconst[3] - (elements[2 + index] - 30.0) / 3600.0; - if (t < 0.0) - { - t = t + 24.0; - } - if (t >= 24.0) - { - t = t - 24.0; - } - if (t < 10.0) - { - ans = ans + "0"; - } - ans = ans + Math.Floor(t) + ":"; - t = (t * 60.0) - 60.0 * Math.Floor(t); - if (t < 10.0) - { - ans = ans + "0"; - } - ans = ans + Math.Floor(t); - if (circumstances[5] == 2) - { - return ans; //RETURNED IN ITAL DETERMINE WHY - } - else - { - return ans; - } - } - // Get the altitude - private static string GetAlt(double[] circumstances) - { - double t; - string ans = ""; - t = circumstances[4] * 180.0 / Math.PI; - t = Math.Floor(t + 0.5); - if (t < 0.0) - { - ans = "-"; - t = -t; - } - else - { - ans = "+"; - } - if (t < 10.0) - { - ans = ans + "0"; - } - ans = ans + t; - if (circumstances[5] == 2) - { - return ans; //returned in italics determine why - - } - else - { - return ans; - } - } + //CURRENT RANGE 1601-2600. + internal class LunarEclipseCalc { + public static List> CalculateLunarEclipse(DateTime d, Double latRad, Double longRad) => Calculate(d, latRad, longRad); + public static List CalculateLunarEclipse(DateTime d, Double latRad, Double longRad, Double[] events) { + List> evs = Calculate(d, latRad, longRad, events); + List deetsList = new List(); + foreach (List ls in evs) { + LunarEclipseDetails deets = new LunarEclipseDetails(ls); + deetsList.Add(deets); + } + return deetsList; } + public static List> CalculateLunarEclipse(DateTime d, Coordinate coord) => Calculate(d, coord.Latitude.ToRadians(), coord.Longitude.ToRadians()); + + + // CALCULATE! + private static List> Calculate(DateTime d, Double latRad, Double longRad, Double[] ev = null) { + //DECLARE ARRAYS + Double[] obsvconst = new Double[6]; + Double[] mid = new Double[41]; + Double[] p1 = new Double[41]; + Double[] u1 = new Double[41]; + Double[] u2 = new Double[41]; + Double[] u3 = new Double[41]; + Double[] u4 = new Double[41]; + Double[] p4 = new Double[41]; + + Double[] el = ev ?? Eclipse.LunarData.LunarDateData(d); + List> events = new List>(); + ReadData(latRad, longRad, obsvconst); + + for (Int32 i = 0; i < el.Length; i += 22) { + if (el[5 + i] <= obsvconst[5]) { + List values = new List(); + obsvconst[4] = i; + GetAll(el, obsvconst, mid, p1, u1, u2, u3, u4, p4); + // Is there an event... + if (mid[5] != 1) { + + values.Add(GetDate(el, p1, obsvconst)); + + if (el[5 + i] == 1) { + values.Add("T"); + } else if (el[5 + i] == 2) { + values.Add("P"); + } else { + values.Add("N"); + } + + // Pen. Mag + values.Add(el[3 + i].ToString()); + + // Umbral Mag + values.Add(el[4 + i].ToString()); + + // P1 + values.Add(GetTime(el, p1, obsvconst)); + + // P1 alt + values.Add(GetAlt(p1)); + + if (u1[5] == 1) { + values.Add("-"); + values.Add("-"); + } else { + // U1 + values.Add(GetTime(el, u1, obsvconst)); + + // U1 alt + values.Add(GetAlt(u1)); + } + if (u2[5] == 1) { + values.Add("-"); + values.Add("-"); + } else { + // U2 + values.Add(GetTime(el, u2, obsvconst)); + + // U2 alt + values.Add(GetAlt(u2)); + } + // mid + + values.Add(GetTime(el, mid, obsvconst)); + + // mid alt + + values.Add(GetAlt(mid)); + + if (u3[5] == 1) { + values.Add("-"); + values.Add("-"); + } else { + // u3 + values.Add(GetTime(el, u3, obsvconst)); + + // u3 alt + values.Add(GetAlt(u3)); + } + if (u4[5] == 1) { + values.Add("-"); + values.Add("-"); + } else { + // u4 + values.Add(GetTime(el, u4, obsvconst)); + + // u4 alt + values.Add(GetAlt(u4)); + + } + // P4 + values.Add(GetTime(el, p4, obsvconst)); + + // P4 alt + values.Add(GetAlt(p4)); + events.Add(values); + } + } + } + return events; + } + // Read the data that's in the form, and populate the obsvconst array + private static void ReadData(Double latRad, Double longRad, Double[] obsvconst) { + + // Get the latitude + obsvconst[0] = latRad; + + // Get the longitude + obsvconst[1] = -1 * longRad; //PASS REVERSE RADIAN. + + // Get the altitude + obsvconst[2] = 100; //CHANGE TO ALLOW USER TO PASS. + + // Get the time zone + obsvconst[3] = 0; //GMT TIME + + obsvconst[4] = 0; //INDEX + + //SET MAX ECLIPSE TYPE + obsvconst[5] = 4;//4 is ALL Eclipses + + } + // Populate the p1, u1, u2, mid, u3, u4 and p4 arrays + private static void GetAll(Double[] elements, Double[] obsvconst, Double[] mid, Double[] p1, Double[] u1, Double[] u2, Double[] u3, Double[] u4, Double[] p4) { + Int32 index = (Int32)obsvconst[4]; + p1[1] = elements[index + 9]; + PopulateCircumstances(elements, p1, obsvconst); + mid[1] = elements[index + 12]; + PopulateCircumstances(elements, mid, obsvconst); + p4[1] = elements[index + 15]; + PopulateCircumstances(elements, p4, obsvconst); + if (elements[index + 5] < 3) { + u1[1] = elements[index + 10]; + PopulateCircumstances(elements, u1, obsvconst); + u4[1] = elements[index + 14]; + PopulateCircumstances(elements, u4, obsvconst); + if (elements[index + 5] < 2) { + u2[1] = elements[index + 11]; + u3[1] = elements[index + 13]; + PopulateCircumstances(elements, u2, obsvconst); + PopulateCircumstances(elements, u3, obsvconst); + } else { + u2[5] = 1; + u3[5] = 1; + } + } else { + u1[5] = 1; + u2[5] = 1; + u3[5] = 1; + u4[5] = 1; + } + if (p1[5] != 0 && u1[5] != 0 && u2[5] != 0 && mid[5] != 0 && u3[5] != 0 && u4[5] != 0 && p4[5] != 0) { + mid[5] = 1; + } + } + // Populate the circumstances array + // entry condition - circumstances[1] must contain the correct value + private static void PopulateCircumstances(Double[] elements, Double[] circumstances, Double[] obsvconst) { + Double t, ra, dec, h; + + Int32 index = (Int32)obsvconst[4]; + t = circumstances[1]; + ra = elements[18 + index] * t + elements[17 + index]; + ra = ra * t + elements[16 + index]; + dec = elements[21 + index] * t + elements[20 + index]; + dec = dec * t + elements[19 + index]; + dec = dec * Math.PI / 180.0; + circumstances[3] = dec; + h = 15.0 * (elements[6 + index] + (t - elements[2 + index] / 3600.0) * 1.00273791) - ra; + h = h * Math.PI / 180.0 - obsvconst[1]; + circumstances[2] = h; + circumstances[4] = Math.Asin(Math.Sin(obsvconst[0]) * Math.Sin(dec) + Math.Cos(obsvconst[0]) * Math.Cos(dec) * Math.Cos(h)); + circumstances[4] -= Math.Asin(Math.Sin(elements[7 + index] * Math.PI / 180.0) * Math.Cos(circumstances[4])); + if (circumstances[4] * 180.0 / Math.PI < elements[8 + index] - 0.5667) { + circumstances[5] = 2; + } else if (circumstances[4] < 0.0) { + circumstances[4] = 0.0; + circumstances[5] = 0; + } else { + circumstances[5] = 0; + } + } + // Get the date of an event + private static String GetDate(Double[] elements, Double[] circumstances, Double[] obsvconst) { + String[] month = new String[] { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };//Month string array + Double t, jd, a, b, c, d, e; + Int32 index = (Int32)obsvconst[4]; + // Calculate the JD for noon (TDT) the day before the day that contains T0 + jd = Math.Floor(elements[index] - elements[1 + index] / 24.0); + // Calculate the local time (ie the offset in hours since midnight TDT on the day containing T0). + t = circumstances[1] + elements[1 + index] - obsvconst[3] - (elements[2 + index] - 30.0) / 3600.0; + + if (t < 0.0) { + jd--; + } + if (t >= 24.0) { + jd++; + } + if (jd >= 2299160.0) { + a = Math.Floor((jd - 1867216.25) / 36524.25); + a = jd + 1 + a - Math.Floor(a / 4.0); + } else { + a = jd; + } + b = a + 1525.0; + c = Math.Floor((b - 122.1) / 365.25); + d = Math.Floor(365.25 * c); + e = Math.Floor((b - d) / 30.6001); + d = b - d - Math.Floor(30.6001 * e); + e = e < 13.5 ? e - 1 : e - 13; + Double year; + String ans; + if (e > 2.5) { + ans = c - 4716 + "-"; + year = c - 4716; + } else { + ans = c - 4715 + "-"; + year = c - 4715; + } + String m = month[(Int32)e - 1]; + ans += m + "-"; + if (d < 10) { + ans += "0"; + } + ans += d; + //Leap Year Integrity Check + + if (m == "Feb" && d == 29 && !DateTime.IsLeapYear((Int32)year)) { + ans = year.ToString() + "-Mar-01"; + } + return ans; + } + // Get the time of an event + private static String GetTime(Double[] elements, Double[] circumstances, Double[] obsvconst) { + Double t; + String ans = ""; + + Int32 index = (Int32)obsvconst[4]; + t = circumstances[1] + elements[1 + index] - obsvconst[3] - (elements[2 + index] - 30.0) / 3600.0; + if (t < 0.0) { + t += 24.0; + } + if (t >= 24.0) { + t -= 24.0; + } + if (t < 10.0) { + ans += "0"; + } + ans = ans + Math.Floor(t) + ":"; + t = t * 60.0 - 60.0 * Math.Floor(t); + if (t < 10.0) { + ans += "0"; + } + ans += Math.Floor(t); + if (circumstances[5] == 2) { + return ans; //RETURNED IN ITAL DETERMINE WHY + } else { + return ans; + } + } + // Get the altitude + private static String GetAlt(Double[] circumstances) { + Double t; + t = circumstances[4] * 180.0 / Math.PI; + t = Math.Floor(t + 0.5); + String ans; + if (t < 0.0) { + ans = "-"; + t = -t; + } else { + ans = "+"; + } + if (t < 10.0) { + ans += "0"; + } + ans += t; + if (circumstances[5] == 2) { + return ans; //returned in italics determine why + + } else { + return ans; + } + } + } } diff --git a/CoordinateSharp/Celestial.MeeusTables.cs b/CoordinateSharp/Celestial.MeeusTables.cs index b5cc431..1f1cc4a 100644 --- a/CoordinateSharp/Celestial.MeeusTables.cs +++ b/CoordinateSharp/Celestial.MeeusTables.cs @@ -1,15 +1,10 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -namespace CoordinateSharp -{ - internal partial class MeeusTables +namespace CoordinateSharp { + internal partial class MeeusTables { + //Ch 47 + private static readonly Double[] Table47A_Arguments = new Double[] { - //Ch 47 - private static double[] Table47A_Arguments = new double[] - { 0,0,1,0, 2,0,-1,0, 2,0,0,0, @@ -71,9 +66,9 @@ namespace CoordinateSharp 1,1,-1,0, 2,0,3,0, 2,0,-1,-2 - }; - private static double[] Table47B_Arguments = new double[] - { + }; + private static readonly Double[] Table47B_Arguments = new Double[] + { 0,0,0,1, 0,0,1,1, 0,0,1,-1, @@ -135,9 +130,9 @@ namespace CoordinateSharp 1,0,-1,-1, 4,-1,0,-1, 2,-2,0,1, - }; - private static double[] Table47A_El_Er = new double[] - { + }; + private static readonly Double[] Table47A_El_Er = new Double[] + { //El 6288774, 1274027,658314,213618,-185116,-114332,58793,57066,53322,45758, -40923,-34720,-30383,15327,-12528,10980,10675,10034,8548,-7888,-6766,-5163, @@ -150,72 +145,58 @@ namespace CoordinateSharp -12831,-10445,-11650,14403,-7003,0,10056,6322,-9884,5751,0,-4950,4130,0,-3958,0,3258, 2616,-1897,-2117,2354,0,0,-1423,-1117,-1571,-1739,0,-4421,0,0,0,0,1165,0,0,8752 - }; - private static double[] Table47B_Eb = new double[] - { + }; + private static readonly Double[] Table47B_Eb = new Double[] + { 5128122,280602,277693,173237,55413,46271,32573,17198,9266,8822, 8216,4324,4200,-3359,2463,2211,2065,-1870,1828,-1794,-1749,-1565,-1491, -1475,-1410,-1344,-1335,1107,1021,833, 777,671,607,596,491,-451,439,422,421,-366,-351,331,315,302,-283,-229, 223,223,-220,-220,-185,181,-177,176,166,-164,132,-119,115,107 - }; - private static double Get_Table47A_Values(double[] values, int l, double t, bool sine) - { - //sine true returns El - //sine false return Er - //Er values start at 60 in the Table47A_El_Er array. + }; + private static Double Get_Table47A_Values(Double[] values, Int32 l, Double t, Boolean sine) { + //sine true returns El + //sine false return Er + //Er values start at 60 in the Table47A_El_Er array. - int nl = l * 4; + Int32 nl = l * 4; - if (sine) - { - double e = 1; + if (sine) { + Double e = 1; - if (Table47A_Arguments[nl + 1] != 0) - { - e = 1 - .002516 * t - .0000074 * Math.Pow(t, 2); + if (Table47A_Arguments[nl + 1] != 0) { + e = 1 - .002516 * t - .0000074 * Math.Pow(t, 2); - if (Math.Abs(Table47A_Arguments[nl + 1]) == 2) - { - e *= e; - } - } - return (Table47A_El_Er[l] * e) * Math.Sin(Table47A_Arguments[nl] * values[0] + Table47A_Arguments[nl + 1] * values[1] + - Table47A_Arguments[nl + 2] * values[2] + Table47A_Arguments[nl + 3] * values[3]); - } - else - { - double e = 1; - if (Table47A_Arguments[nl + 1] != 0) - { - e = 1 - .002516 * t - .0000074 * Math.Pow(t, 2); - - if (Math.Abs(Table47A_Arguments[nl + 1]) == 2) - { - e *= e; - } - } - return (Table47A_El_Er[l + 60] * e) * Math.Cos(Table47A_Arguments[nl] * values[0] + Table47A_Arguments[nl + 1] * values[1] + - Table47A_Arguments[nl + 2] * values[2] + Table47A_Arguments[nl + 3] * values[3]); - } + if (Math.Abs(Table47A_Arguments[nl + 1]) == 2) { + e *= e; + } } - private static double Get_Table47B_Values(double[] values, int l, double t) - { - int nl = l * 4; - double e = 1; + return Table47A_El_Er[l] * e * Math.Sin(Table47A_Arguments[nl] * values[0] + Table47A_Arguments[nl + 1] * values[1] + Table47A_Arguments[nl + 2] * values[2] + Table47A_Arguments[nl + 3] * values[3]); + } else { + Double e = 1; + if (Table47A_Arguments[nl + 1] != 0) { + e = 1 - .002516 * t - .0000074 * Math.Pow(t, 2); - if (Table47B_Arguments[nl + 1] != 0) - { - e = 1 - .002516 * t - .0000074 * Math.Pow(t, 2); - - if (Math.Abs(Table47B_Arguments[nl + 1]) == 2) - { - e *= e; - } - } - return (Table47B_Eb[l] * e) * Math.Sin(Table47B_Arguments[nl] * values[0] + Table47B_Arguments[nl + 1] * values[1] + - Table47B_Arguments[nl + 2] * values[2] + Table47B_Arguments[nl + 3] * values[3]); + if (Math.Abs(Table47A_Arguments[nl + 1]) == 2) { + e *= e; + } } + return Table47A_El_Er[l + 60] * e * Math.Cos(Table47A_Arguments[nl] * values[0] + Table47A_Arguments[nl + 1] * values[1] + Table47A_Arguments[nl + 2] * values[2] + Table47A_Arguments[nl + 3] * values[3]); + } } + private static Double Get_Table47B_Values(Double[] values, Int32 l, Double t) { + Int32 nl = l * 4; + Double e = 1; + + if (Table47B_Arguments[nl + 1] != 0) { + e = 1 - .002516 * t - .0000074 * Math.Pow(t, 2); + + if (Math.Abs(Table47B_Arguments[nl + 1]) == 2) { + e *= e; + } + } + return Table47B_Eb[l] * e * Math.Sin(Table47B_Arguments[nl] * values[0] + Table47B_Arguments[nl + 1] * values[1] + Table47B_Arguments[nl + 2] * values[2] + Table47B_Arguments[nl + 3] * values[3]); + } + } } diff --git a/CoordinateSharp/Celestial.MoonCalculations.cs b/CoordinateSharp/Celestial.MoonCalculations.cs index 972a266..977bd0f 100644 --- a/CoordinateSharp/Celestial.MoonCalculations.cs +++ b/CoordinateSharp/Celestial.MoonCalculations.cs @@ -1,872 +1,759 @@ using System; using System.Collections.Generic; -namespace CoordinateSharp -{ - internal class MoonCalc +namespace CoordinateSharp { + internal class MoonCalc { + static readonly Double rad = Math.PI / 180; //For converting radians + + //obliquity of the ecliptic in radians based on standard equinox 2000. + static readonly Double e = rad * 23.4392911; + /// + /// Gets Moon Times, Altitude and Azimuth + /// + /// Date + /// Latitude + /// Longitude + /// Celestial + public static void GetMoonTimes(DateTime date, Double lat, Double lng, Celestial c) { + //Get current Moon Position to populate passed Alt / Azi for user specified date + MoonPosition mp = GetMoonPosition(date, lat, lng, c); + Double altRad = mp.Altitude / Math.PI * 180; //Convert alt to degrees + c.moonAltitude = altRad - mp.ParallaxCorection; //Set altitude with adjusted parallax + c.moonAzimuth = mp.Azimuth / Math.PI * 180 + 180; //Azimuth in degrees + 180 for E by N. + + ////New Iterations for Moon set / rise + Boolean moonRise = false; + Boolean moonSet = false; + + //Start at beginning of day + DateTime t = new DateTime(date.Year, date.Month, date.Day, 0, 0, 0, DateTimeKind.Utc); + + //Get start of day Moon Pos + MoonPosition moonPos = GetMoonPosition(t, lat, lng, c); + Double alt1 = moonPos.Altitude - moonPos.ParallaxCorection * rad; + + DateTime? setTime = null; + DateTime? riseTime = null; + Double hz = -.3 * rad;//Horizon degrees at -.3 for appearant rise / set + + //Iterate for each hour of the day + for (Int32 x = 1; x <= 24; x++) { + moonPos = GetMoonPosition(t.AddHours(x), lat, lng, c);//Get the next hours altitude for comparison + Double alt2 = moonPos.Altitude - moonPos.ParallaxCorection * rad; + //If hour 1 is below horizon and hour 2 is above + if (alt1 < hz && alt2 >= hz) { + //Moon Rise Occurred + moonRise = true; + DateTime dt1 = t.AddHours(x - 1); + moonPos = GetMoonPosition(dt1, lat, lng, c);//Get the next hours altitude for comparison + Double altM1 = moonPos.Altitude - moonPos.ParallaxCorection * rad; + //Iterate through each minute to determine at which minute the horizon is crossed. + //Interpolation is more efficient, but yielded results with deviations up to 5 minutes. + //Investigate formula efficiency + for (Int32 y = 1; y <= 60; y++) { + DateTime dt2 = t.AddHours(x - 1).AddMinutes(y); + moonPos = GetMoonPosition(dt2, lat, lng, c);//Get the next hours altitude for comparison + Double altM2 = moonPos.Altitude - moonPos.ParallaxCorection * rad; + if (altM1 < hz && altM2 >= hz) { + //interpolate seconds + Double p = 60 * ((hz - altM1) / (altM2 - altM1)); + riseTime = dt1.AddMinutes(y - 1).AddSeconds(p); + break; + } + altM1 = altM2; + + } + } + //if hour 2 is above horizon and hour 1 below + if (alt1 >= hz && alt2 < hz) { + //Moon Set Occured + moonSet = true; + DateTime dt1 = t.AddHours(x - 1); + moonPos = GetMoonPosition(dt1, lat, lng, c);//Get the next hours altitude for comparison + Double altM1 = moonPos.Altitude - moonPos.ParallaxCorection * rad; + //Iterate through each minute to determine at which minute the horizon is crossed. + //Interpolation is more efficient, but yielded results with deviations up to 5 minutes. + //Investigate formula efficiency + for (Int32 y = 1; y <= 60; y++) { + DateTime dt2 = t.AddHours(x - 1).AddMinutes(y); + moonPos = GetMoonPosition(dt2, lat, lng, c);//Get the next hours altitude for comparison + Double altM2 = moonPos.Altitude - moonPos.ParallaxCorection * rad; + if (altM1 >= hz && altM2 < hz) { + //Interpolate seconds + Double p = 60 * ((hz - altM2) / (altM1 - altM2)); + setTime = dt1.AddMinutes(y).AddSeconds(-p); + break; + } + altM1 = altM2; + + } + } + alt1 = alt2; + if (moonRise && moonSet) { break; } + } + + c.moonSet = setTime; + c.moonRise = riseTime; + if (moonRise && moonSet) { c.moonCondition = CelestialStatus.RiseAndSet; } else { + if (!moonRise && !moonSet) { + c.moonCondition = alt1 >= 0 ? CelestialStatus.UpAllDay : CelestialStatus.DownAllDay; + } + if (!moonRise && moonSet) { c.moonCondition = CelestialStatus.NoRise; } + if (moonRise && !moonSet) { c.moonCondition = CelestialStatus.NoSet; } + } + } + + private static MoonPosition GetMoonPosition(DateTime date, Double lat, Double lng, Celestial cel) { + //Set UTC date integrity + date = new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second, DateTimeKind.Utc); + + Double d = JulianConversions.GetJulian_Epoch2000(date); + + //Ch 47 + Double JDE = JulianConversions.GetJulian(date);//Get julian + + Double T = (JDE - 2451545) / 36525; //Get dynamic time. + Double[] LDMNF = Get_Moon_LDMNF(T); + CelCoords c = GetMoonCoords(d, cel, LDMNF, T); + Distance dist = GetMoonDistance(date); + Double lw = rad * -lng; + Double phi = rad * lat; + + Double H = rad * MeeusFormulas.Get_Sidereal_Time(JDE) - lw - c.Ra; + + Double ra = c.Ra; //Adjust current RA formula to avoid needless RAD conversions + Double dec = c.Dec; //Adjust current RA formula to avoid needless RAD conversions + + //Adjust for parallax (low accuracry increases may not be worth cost) + //Investigate + Double pSinE = Get_pSinE(dec, dist.Meters) * Math.PI / 180; + Double pCosE = Get_pCosE(dec, dist.Meters) * Math.PI / 180; + Double cRA = Parallax_RA(dist.Meters, H, pCosE, dec, ra); + Double tDEC = Parallax_Dec(dist.Meters, H, pCosE, pSinE, dec, cRA); + //Double tRA = ra - cRA; + dec = tDEC; + //ra = tRA; + + //Get true altitude + Double h = Altitude(H, phi, dec); + + // formula 14.1 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998. + Double pa = Math.Atan2(Math.Sin(H), Math.Tan(phi) * Math.Cos(dec) - Math.Sin(dec) * Math.Cos(H)); + + //altitude correction for refraction + h += AstroRefraction(h); + + MoonPosition mp = new MoonPosition { + Azimuth = Azimuth(H, phi, dec), + Altitude = h / Math.PI * 180, + Distance = dist, + ParallacticAngle = pa + }; + + Double horParal = 8.794 / (dist.Meters / 149.59787E6); // horizontal parallax (arcseconds), Meeus S. 263 + Double p = Math.Asin(Math.Cos(h) * Math.Sin(horParal / 3600)); // parallax in altitude (degrees) + p *= 1000; + + mp.ParallaxCorection = p; + mp.Altitude *= rad; + + return mp; + } + private static CelCoords GetMoonCoords(Double _1, Celestial _2, Double[] LDMNF, Double t) { + // Legacy function. Updated with Meeus Calcs for increased accuracy. + // geocentric ecliptic coordinates of the moon + // Meeus Ch 47 + Double[] cs = Get_Moon_Coordinates(LDMNF, t); + + Double l = cs[0]; // longitude + Double b = cs[1]; // latitude + + CelCoords mc = new CelCoords { + Ra = RightAscension(l, b), + Dec = Declination(l, b) + }; + //Double ra = mc.ra / Math.PI * 180; + //Double dec = mc.dec / Math.PI * 180; + + return mc; + } + + public static void GetMoonIllumination(DateTime date, Celestial c, Double lat, Double lng) { + date = new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second, DateTimeKind.Utc); + + Double d = JulianConversions.GetJulian_Epoch2000(date); + CelCoords s = GetSunCoords(d); + Double JDE = JulianConversions.GetJulian(date);//Get julian + Double T = (JDE - 2451545) / 36525; //Get dynamic time. + Double[] LDMNF = Get_Moon_LDMNF(T); + + CelCoords m = GetMoonCoords(d, c, LDMNF, T); + + Double sdist = 149598000, + phi = Math.Acos(Math.Sin(s.Dec) * Math.Sin(m.Dec) + Math.Cos(s.Dec) * Math.Cos(m.Dec) * Math.Cos(s.Ra - m.Ra)), + inc = Math.Atan2(sdist * Math.Sin(phi), m.Dist - sdist * Math.Cos(phi)), + angle = Math.Atan2(Math.Cos(s.Dec) * Math.Sin(s.Ra - m.Ra), Math.Sin(s.Dec) * Math.Cos(m.Dec) - + Math.Cos(s.Dec) * Math.Sin(m.Dec) * Math.Cos(s.Ra - m.Ra)); + + + MoonIllum mi = new MoonIllum { + Fraction = (1 + Math.Cos(inc)) / 2, + Phase = 0.5 + 0.5 * inc * (angle < 0 ? -1 : 1) / Math.PI, + Angle = angle + }; + + + c.moonIllum = mi; + + String moonName = ""; + Int32 moonDate = 0; + //GET PHASE NAME + + //CHECK MOON AT BEGINNING AT END OF DAY TO GET DAY PHASE + DateTime dMon = new DateTime(date.Year, date.Month, 1); + for (Int32 x = 1; x <= date.Day; x++) { + DateTime nDate = new DateTime(dMon.Year, dMon.Month, x, 0, 0, 0, DateTimeKind.Utc); + d = JulianConversions.GetJulian_Epoch2000(nDate); + s = GetSunCoords(d); + JDE = JulianConversions.GetJulian(nDate);//Get julian + T = (JDE - 2451545) / 36525; //Get dynamic time. + LDMNF = Get_Moon_LDMNF(T); + m = GetMoonCoords(d, c, LDMNF, T); + + phi = Math.Acos(Math.Sin(s.Dec) * Math.Sin(m.Dec) + Math.Cos(s.Dec) * Math.Cos(m.Dec) * Math.Cos(s.Ra - m.Ra)); + inc = Math.Atan2(sdist * Math.Sin(phi), m.Dist - sdist * Math.Cos(phi)); + angle = Math.Atan2(Math.Cos(s.Dec) * Math.Sin(s.Ra - m.Ra), Math.Sin(s.Dec) * Math.Cos(m.Dec) - + Math.Cos(s.Dec) * Math.Sin(m.Dec) * Math.Cos(s.Ra - m.Ra)); + + Double startPhase = 0.5 + 0.5 * inc * (angle < 0 ? -1 : 1) / Math.PI; + + nDate = new DateTime(dMon.Year, dMon.Month, x, 23, 59, 59, DateTimeKind.Utc); + d = JulianConversions.GetJulian_Epoch2000(nDate); + s = GetSunCoords(d); + JDE = JulianConversions.GetJulian(nDate);//Get julian + T = (JDE - 2451545) / 36525; //Get dynamic time. + LDMNF = Get_Moon_LDMNF(T); + m = GetMoonCoords(d, c, LDMNF, T); + + phi = Math.Acos(Math.Sin(s.Dec) * Math.Sin(m.Dec) + Math.Cos(s.Dec) * Math.Cos(m.Dec) * Math.Cos(s.Ra - m.Ra)); + inc = Math.Atan2(sdist * Math.Sin(phi), m.Dist - sdist * Math.Cos(phi)); + angle = Math.Atan2(Math.Cos(s.Dec) * Math.Sin(s.Ra - m.Ra), Math.Sin(s.Dec) * Math.Cos(m.Dec) - + Math.Cos(s.Dec) * Math.Sin(m.Dec) * Math.Cos(s.Ra - m.Ra)); + + Double endPhase = 0.5 + 0.5 * inc * (angle < 0 ? -1 : 1) / Math.PI; + //Determine Moon Name. + if (startPhase <= .5 && endPhase >= .5) { + moonDate = x; + moonName = GetMoonName(dMon.Month, moonName); + } + //Get Moon Name (month, string); + //Get Moon Phase Name + if (date.Day == x) { + if (startPhase > endPhase) { + mi.PhaseName = "New Moon"; + break; + } + if (startPhase <= .25 && endPhase >= .25) { + mi.PhaseName = "First Quarter"; + break; + } + if (startPhase <= .5 && endPhase >= .5) { + mi.PhaseName = "Full Moon"; + break; + } + if (startPhase <= .75 && endPhase >= .75) { + mi.PhaseName = "Last Quarter"; + break; + } + + if (startPhase > 0 && startPhase < .25 && endPhase > 0 && endPhase < .25) { + mi.PhaseName = "Waxing Crescent"; + break; + } + if (startPhase > .25 && startPhase < .5 && endPhase > .25 && endPhase < .5) { + mi.PhaseName = "Waxing Gibbous"; + break; + } + if (startPhase > .5 && startPhase < .75 && endPhase > .5 && endPhase < .75) { + mi.PhaseName = "Waning Gibbous"; + break; + } + if (startPhase > .75 && startPhase < 1 && endPhase > .75 && endPhase < 1) { + mi.PhaseName = "Waning Crescent"; + break; + } + } + + } + c.AstrologicalSigns.MoonName = date.Day == moonDate ? moonName : ""; + CalculateLunarEclipse(date, lat, lng, c); + + } + public static void CalculateLunarEclipse(DateTime date, Double lat, Double longi, Celestial c) { + //Convert to Radian + Double latR = lat * Math.PI / 180; + Double longR = longi * Math.PI / 180; + List> se = LunarEclipseCalc.CalculateLunarEclipse(date, latR, longR); + //RETURN FIRST AND LAST + if (se.Count == 0) { return; } + //FIND LAST AND NEXT ECLIPSE + Int32 lastE = -1; + Int32 nextE = -1; + Int32 currentE = 0; + DateTime lastDate = new DateTime(); + DateTime nextDate = new DateTime(3300, 1, 1); + //Iterate to get last and next eclipse + + foreach (List values in se) { + DateTime ld = DateTime.ParseExact(values[0], "yyyy-MMM-dd", System.Globalization.CultureInfo.InvariantCulture); + if (ld < date && ld > lastDate) { lastDate = ld; lastE = currentE; } + if (ld >= date && ld < nextDate) { nextDate = ld; nextE = currentE; } + currentE++; + } + //SET ECLIPSE DATA + if (lastE >= 0) { + c.LunarEclipse.LastEclipse = new LunarEclipseDetails(se[lastE]); + } + if (nextE >= 0) { + c.LunarEclipse.NextEclipse = new LunarEclipseDetails(se[nextE]); + } + } + + private static String GetMoonName(Int32 month, String name) => name != "" ? "Blue Moon" : (month switch { - static double rad = Math.PI / 180; //For converting radians + 1 => "Wolf Moon", + 2 => "Snow Moon", + 3 => "Worm Moon", + 4 => "Pink Moon", + 5 => "Flower Moon", + 6 => "Strawberry Moon", + 7 => "Buck Moon", + 8 => "Sturgeon Moon", + 9 => "Corn Moon", + 10 => "Hunters Moon", + 11 => "Beaver Moon", + 12 => "Cold Moon", + _ => "", + }); + public static void GetMoonDistance(DateTime date, Celestial c) { + date = new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second, DateTimeKind.Utc); - //obliquity of the ecliptic in radians based on standard equinox 2000. - static double e = rad * 23.4392911; - /// - /// Gets Moon Times, Altitude and Azimuth - /// - /// Date - /// Latitude - /// Longitude - /// Celestial - public static void GetMoonTimes(DateTime date, double lat, double lng, Celestial c) - { - //Get current Moon Position to populate passed Alt / Azi for user specified date - MoonPosition mp = GetMoonPosition(date, lat, lng, c); - double altRad = mp.Altitude / Math.PI*180; //Convert alt to degrees - c.moonAltitude = (altRad - mp.ParallaxCorection); //Set altitude with adjusted parallax - c.moonAzimuth = mp.Azimuth / Math.PI*180 + 180; //Azimuth in degrees + 180 for E by N. - - ////New Iterations for Moon set / rise - bool moonRise = false; - bool moonSet = false; - - //Start at beginning of day - DateTime t = new DateTime(date.Year, date.Month, date.Day, 0, 0, 0, DateTimeKind.Utc); - - //Get start of day Moon Pos - MoonPosition moonPos = GetMoonPosition(t, lat, lng, c); - double alt1 = moonPos.Altitude - (moonPos.ParallaxCorection * rad); - - DateTime? setTime = null; - DateTime? riseTime = null; - double hz = -.3 * rad;//Horizon degrees at -.3 for appearant rise / set - - //Iterate for each hour of the day - for(int x = 1;x<=24;x++) - { - moonPos = GetMoonPosition(t.AddHours(x), lat, lng, c);//Get the next hours altitude for comparison - double alt2 = moonPos.Altitude - (moonPos.ParallaxCorection * rad); - //If hour 1 is below horizon and hour 2 is above - if(alt1 =hz) - { - //Moon Rise Occurred - moonRise = true; - DateTime dt1 = t.AddHours(x - 1); - moonPos = GetMoonPosition(dt1, lat, lng, c);//Get the next hours altitude for comparison - double altM1 = moonPos.Altitude - (moonPos.ParallaxCorection * rad); - //Iterate through each minute to determine at which minute the horizon is crossed. - //Interpolation is more efficient, but yielded results with deviations up to 5 minutes. - //Investigate formula efficiency - for (int y = 1;y<=60;y++) - { - DateTime dt2 = t.AddHours(x-1).AddMinutes(y); - moonPos = GetMoonPosition(dt2, lat, lng, c);//Get the next hours altitude for comparison - double altM2 = moonPos.Altitude - (moonPos.ParallaxCorection * rad); - if (altM1=hz) - { - //interpolate seconds - double p = 60 * ((hz - altM1) / (altM2 - altM1)); - riseTime = dt1.AddMinutes(y-1).AddSeconds(p); - break; - } - altM1 = altM2; - - } - } - //if hour 2 is above horizon and hour 1 below - if(alt1>=hz && alt2 = hz && altM2 < hz) - { - //Interpolate seconds - double p = 60 * ((hz - altM2) / (altM1 - altM2)); - setTime = dt1.AddMinutes(y).AddSeconds(-p); - break; - } - altM1 = altM2; - - } - } - alt1 = alt2; - if(moonRise && moonSet) { break; } - } - - c.moonSet = setTime; - c.moonRise = riseTime; - if (moonRise && moonSet) { c.moonCondition = CelestialStatus.RiseAndSet; } - else - { - if (!moonRise && !moonSet) - { - if (alt1 >= 0) { c.moonCondition = CelestialStatus.UpAllDay; } - else { c.moonCondition = CelestialStatus.DownAllDay; } - } - if (!moonRise && moonSet) { c.moonCondition = CelestialStatus.NoRise; } - if (moonRise && !moonSet) { c.moonCondition = CelestialStatus.NoSet; } - } - } - - private static MoonPosition GetMoonPosition(DateTime date, double lat, double lng, Celestial cel) - { - //Set UTC date integrity - date = new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second, DateTimeKind.Utc); - - double d = JulianConversions.GetJulian_Epoch2000(date); - - //Ch 47 - double JDE = JulianConversions.GetJulian(date);//Get julian - - double T = (JDE - 2451545) / 36525; //Get dynamic time. - double[] LDMNF = Get_Moon_LDMNF(T); - CelCoords c = GetMoonCoords(d, cel, LDMNF, T); - Distance dist = GetMoonDistance(date); - double lw = rad * -lng; - double phi = rad * lat; - - double H = rad * MeeusFormulas.Get_Sidereal_Time(JDE) - lw - c.ra; - - double ra = c.ra; //Adjust current RA formula to avoid needless RAD conversions - double dec = c.dec; //Adjust current RA formula to avoid needless RAD conversions - - //Adjust for parallax (low accuracry increases may not be worth cost) - //Investigate - double pSinE = Get_pSinE(dec, dist.Meters) * Math.PI / 180; - double pCosE = Get_pCosE(dec, dist.Meters) * Math.PI / 180; - double cRA = Parallax_RA(dist.Meters, H, pCosE, dec, ra); - double tDEC = Parallax_Dec(dist.Meters, H, pCosE, pSinE, dec, cRA); - double tRA = ra - cRA; - dec = tDEC; - ra = tRA; - - //Get true altitude - double h = altitude(H, phi, dec); - - // formula 14.1 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998. - double pa = Math.Atan2(Math.Sin(H), Math.Tan(phi) * Math.Cos(dec) - Math.Sin(dec) * Math.Cos(H)); - - //altitude correction for refraction - h = h + astroRefraction(h); - - MoonPosition mp = new MoonPosition(); - mp.Azimuth = azimuth(H, phi, dec); - mp.Altitude = h / Math.PI * 180; - mp.Distance = dist; - mp.ParallacticAngle = pa; - - double horParal = 8.794 / (dist.Meters / 149.59787E6); // horizontal parallax (arcseconds), Meeus S. 263 - double p = Math.Asin(Math.Cos(h) * Math.Sin(horParal/3600)); // parallax in altitude (degrees) - p *= 1000; - - mp.ParallaxCorection = p; - mp.Altitude *= rad; - - return mp; - } - private static CelCoords GetMoonCoords(double d, Celestial c, double[] LDMNF, double t) - { - // Legacy function. Updated with Meeus Calcs for increased accuracy. - // geocentric ecliptic coordinates of the moon - // Meeus Ch 47 - double[] cs = Get_Moon_Coordinates(LDMNF, t); - - double l = cs[0]; // longitude - double b = cs[1]; // latitude - - CelCoords mc = new CelCoords(); - - mc.ra = rightAscension(l, b); - double ra = mc.ra / Math.PI * 180; - - mc.dec = declination(l, b); - double dec = mc.dec / Math.PI * 180; - - return mc; - } - - public static void GetMoonIllumination(DateTime date, Celestial c, double lat, double lng) - { - date = new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second, DateTimeKind.Utc); - - double d = JulianConversions.GetJulian_Epoch2000(date); - CelCoords s = GetSunCoords(d); - double JDE = JulianConversions.GetJulian(date);//Get julian - double T = (JDE - 2451545) / 36525; //Get dynamic time. - double[] LDMNF = Get_Moon_LDMNF(T); - - CelCoords m = GetMoonCoords(d, c,LDMNF, T); - - double sdist = 149598000, - phi = Math.Acos(Math.Sin(s.dec) * Math.Sin(m.dec) + Math.Cos(s.dec) * Math.Cos(m.dec) * Math.Cos(s.ra - m.ra)), - inc = Math.Atan2(sdist * Math.Sin(phi), m.dist - sdist * Math.Cos(phi)), - angle = Math.Atan2(Math.Cos(s.dec) * Math.Sin(s.ra - m.ra), Math.Sin(s.dec) * Math.Cos(m.dec) - - Math.Cos(s.dec) * Math.Sin(m.dec) * Math.Cos(s.ra - m.ra)); - - - MoonIllum mi = new MoonIllum(); - - mi.Fraction = (1 + Math.Cos(inc)) / 2; - mi.Phase = 0.5 + 0.5 * inc * (angle < 0 ? -1 : 1) / Math.PI; - mi.Angle = angle; - - - c.moonIllum = mi; - - string moonName = ""; - int moonDate = 0; - //GET PHASE NAME - - //CHECK MOON AT BEGINNING AT END OF DAY TO GET DAY PHASE - DateTime dMon = new DateTime(date.Year, date.Month, 1); - for(int x = 1;x<= date.Day;x++) - { - DateTime nDate = new DateTime(dMon.Year, dMon.Month, x, 0, 0, 0, DateTimeKind.Utc); - d = JulianConversions.GetJulian_Epoch2000(nDate); - s = GetSunCoords(d); - JDE = JulianConversions.GetJulian(nDate);//Get julian - T = (JDE - 2451545) / 36525; //Get dynamic time. - LDMNF = Get_Moon_LDMNF(T); - m = GetMoonCoords(d, c,LDMNF,T); - - phi = Math.Acos(Math.Sin(s.dec) * Math.Sin(m.dec) + Math.Cos(s.dec) * Math.Cos(m.dec) * Math.Cos(s.ra - m.ra)); - inc = Math.Atan2(sdist * Math.Sin(phi), m.dist - sdist * Math.Cos(phi)); - angle = Math.Atan2(Math.Cos(s.dec) * Math.Sin(s.ra - m.ra), Math.Sin(s.dec) * Math.Cos(m.dec) - - Math.Cos(s.dec) * Math.Sin(m.dec) * Math.Cos(s.ra - m.ra)); - - double startPhase = 0.5 + 0.5 * inc * (angle < 0 ? -1 : 1) / Math.PI; - - nDate = new DateTime(dMon.Year, dMon.Month, x, 23, 59, 59, DateTimeKind.Utc); - d = JulianConversions.GetJulian_Epoch2000(nDate); - s = GetSunCoords(d); - JDE = JulianConversions.GetJulian(nDate);//Get julian - T = (JDE - 2451545) / 36525; //Get dynamic time. - LDMNF = Get_Moon_LDMNF(T); - m = GetMoonCoords(d, c,LDMNF,T); - - phi = Math.Acos(Math.Sin(s.dec) * Math.Sin(m.dec) + Math.Cos(s.dec) * Math.Cos(m.dec) * Math.Cos(s.ra - m.ra)); - inc = Math.Atan2(sdist * Math.Sin(phi), m.dist - sdist * Math.Cos(phi)); - angle = Math.Atan2(Math.Cos(s.dec) * Math.Sin(s.ra - m.ra), Math.Sin(s.dec) * Math.Cos(m.dec) - - Math.Cos(s.dec) * Math.Sin(m.dec) * Math.Cos(s.ra - m.ra)); - - double endPhase = 0.5 + 0.5 * inc * (angle < 0 ? -1 : 1) / Math.PI; - //Determine Moon Name. - if (startPhase <= .5 && endPhase >= .5) - { - moonDate = x; - moonName = GetMoonName(dMon.Month, moonName); - } - //Get Moon Name (month, string); - //Get Moon Phase Name - if (date.Day == x) - { - if (startPhase > endPhase) - { - mi.PhaseName = "New Moon"; - break; - } - if (startPhase <= .25 && endPhase >= .25) - { - mi.PhaseName = "First Quarter"; - break; - } - if (startPhase <= .5 && endPhase >= .5) - { - mi.PhaseName = "Full Moon"; - break; - } - if (startPhase <= .75 && endPhase >= .75) - { - mi.PhaseName = "Last Quarter"; - break; - } - - if (startPhase > 0 && startPhase < .25 && endPhase > 0 && endPhase < .25) - { - mi.PhaseName = "Waxing Crescent"; - break; - } - if (startPhase > .25 && startPhase < .5 && endPhase > .25 && endPhase < .5) - { - mi.PhaseName = "Waxing Gibbous"; - break; - } - if (startPhase > .5 && startPhase < .75 && endPhase > .5 && endPhase < .75) - { - mi.PhaseName = "Waning Gibbous"; - break; - } - if (startPhase > .75 && startPhase < 1 && endPhase > .75 && endPhase < 1) - { - mi.PhaseName = "Waning Crescent"; - break; - } - } - - } - if (date.Day == moonDate) - { - c.AstrologicalSigns.MoonName = moonName; - } - else { c.AstrologicalSigns.MoonName = ""; } - CalculateLunarEclipse(date, lat, lng, c); - - } - public static void CalculateLunarEclipse(DateTime date, double lat, double longi, Celestial c) - { - //Convert to Radian - double latR = lat * Math.PI / 180; - double longR = longi * Math.PI / 180; - List> se = LunarEclipseCalc.CalculateLunarEclipse(date, latR, longR); - //RETURN FIRST AND LAST - if (se.Count == 0) { return; } - //FIND LAST AND NEXT ECLIPSE - int lastE = -1; - int nextE = -1; - int currentE = 0; - DateTime lastDate = new DateTime(); - DateTime nextDate = new DateTime(3300, 1, 1); - //Iterate to get last and next eclipse - - foreach (List values in se) - { - DateTime ld = DateTime.ParseExact(values[0], "yyyy-MMM-dd", System.Globalization.CultureInfo.InvariantCulture); - if (ld < date && ld > lastDate) { lastDate = ld; lastE = currentE; } - if (ld >= date && ld < nextDate) { nextDate = ld; nextE = currentE; } - currentE++; - } - //SET ECLIPSE DATA - if (lastE >= 0) - { - c.LunarEclipse.LastEclipse = new LunarEclipseDetails(se[lastE]); - } - if (nextE >= 0) - { - c.LunarEclipse.NextEclipse = new LunarEclipseDetails(se[nextE]); - } - } - - private static string GetMoonName(int month, string name) - { - if (name != "") { return "Blue Moon"; } - switch (month) - { - case 1: - return "Wolf Moon"; - case 2: - return "Snow Moon"; - case 3: - return "Worm Moon"; - case 4: - return "Pink Moon"; - case 5: - return "Flower Moon"; - case 6: - return "Strawberry Moon"; - case 7: - return "Buck Moon"; - case 8: - return "Sturgeon Moon"; - case 9: - return "Corn Moon"; - case 10: - return "Hunters Moon"; - case 11: - return "Beaver Moon"; - case 12: - return "Cold Moon"; - default: - return ""; - } - } - public static void GetMoonDistance(DateTime date, Celestial c) - { - date = new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second, DateTimeKind.Utc); - - c.moonDistance = GetMoonDistance(date); //Updating distance formula - } - //Moon Time Functions - private static CelCoords GetSunCoords(double d) - { - double M = solarMeanAnomaly(d), - L = eclipticLongitude(M); - CelCoords c = new CelCoords(); - c.dec = declination(L, 0); - c.ra = rightAscension(L, 0); - return c; - } - private static double solarMeanAnomaly(double d) { return rad * (357.5291 + 0.98560028 * d); } - private static double eclipticLongitude(double M) - { - double C = rad * (1.9148 * Math.Sin(M) + 0.02 * Math.Sin(2 * M) + 0.0003 * Math.Sin(3 * M)), // equation of center + c.moonDistance = GetMoonDistance(date); //Updating distance formula + } + //Moon Time Functions + private static CelCoords GetSunCoords(Double d) { + Double M = SolarMeanAnomaly(d), + L = EclipticLongitude(M); + CelCoords c = new CelCoords { + Dec = Declination(L, 0), + Ra = RightAscension(L, 0) + }; + return c; + } + private static Double SolarMeanAnomaly(Double d) => rad * (357.5291 + 0.98560028 * d); + private static Double EclipticLongitude(Double M) { + Double C = rad * (1.9148 * Math.Sin(M) + 0.02 * Math.Sin(2 * M) + 0.0003 * Math.Sin(3 * M)), // equation of center P = rad * 102.9372; // perihelion of the Earth - return M + C + P + Math.PI; - } + return M + C + P + Math.PI; + } - public static void GetMoonSign(DateTime date, Celestial c) - { - //Formulas taken from https://www.astrocal.co.uk/moon-sign-calculator/ - double d = date.Day; - double m = date.Month; - double y = date.Year; - double hr = date.Hour; - double mi = date.Minute; + public static void GetMoonSign(DateTime date, Celestial c) { + //Formulas taken from https://www.astrocal.co.uk/moon-sign-calculator/ + Double d = date.Day; + Double m = date.Month; + Double y = date.Year; + Double hr = date.Hour; + Double mi = date.Minute; - double f = hr + (mi / 60); - double im = 12 * (y + 4800) + m - 3; - double j = (2 * (im - Math.Floor(im / 12) * 12) + 7 + 365 * im) / 12; - j = Math.Floor(j) + d + Math.Floor(im / 48) - 32083; - double jd = j + Math.Floor(im / 4800) - Math.Floor(im / 1200) + 38; - double T = ((jd - 2415020) + f / 24 - .5) / 36525; - double ob = FNr(23.452294 - .0130125 * T); - double ll = 973563 + 1732564379 * T - 4 * T * T; - double g = 1012395 + 6189 * T; - double n = 933060 - 6962911 * T + 7.5 * T * T; - double g1 = 1203586 + 14648523 * T - 37 * T * T; - d = 1262655 + 1602961611 * T - 5 * T * T; - double M = 3600; - double l = (ll - g1) / M; - double l1 = ((ll - d) - g) / M; - f = (ll - n) / M; - d = d / M; - y = 2 * d; - double ml = 22639.6 * FNs(l) - 4586.4 * FNs(l - y); - ml = ml + 2369.9 * FNs(y) + 769 * FNs(2 * l) - 669 * FNs(l1); - ml = ml - 411.6 * FNs(2 * f) - 212 * FNs(2 * l - y); - ml = ml - 206 * FNs(l + l1 - y) + 192 * FNs(l + y); - ml = ml - 165 * FNs(l1 - y) + 148 * FNs(l - l1) - 125 * FNs(d); - ml = ml - 110 * FNs(l + l1) - 55 * FNs(2 * f - y); - ml = ml - 45 * FNs(l + 2 * f) + 40 * FNs(l - 2 * f); - double tn = n + 5392 * FNs(2 * f - y) - 541 * FNs(l1) - 442 * FNs(y); - tn = tn + 423 * FNs(2 * f) - 291 * FNs(2 * l - 2 * f); - g = FNu(FNp(ll + ml)); - double sign = Math.Floor(g / 30); - double degree = (g - (sign * 30)); - sign = sign + 1; + Double f = hr + mi / 60; + Double im = 12 * (y + 4800) + m - 3; + Double j = (2 * (im - Math.Floor(im / 12) * 12) + 7 + 365 * im) / 12; + j = Math.Floor(j) + d + Math.Floor(im / 48) - 32083; + Double jd = j + Math.Floor(im / 4800) - Math.Floor(im / 1200) + 38; + Double T = (jd - 2415020 + f / 24 - .5) / 36525; + //Double ob = FNr(23.452294 - .0130125 * T); + Double ll = 973563 + 1732564379 * T - 4 * T * T; + Double g = 1012395 + 6189 * T; + Double n = 933060 - 6962911 * T + 7.5 * T * T; + Double g1 = 1203586 + 14648523 * T - 37 * T * T; + d = 1262655 + 1602961611 * T - 5 * T * T; + Double M = 3600; + Double l = (ll - g1) / M; + Double l1 = (ll - d - g) / M; + f = (ll - n) / M; + d /= M; + y = 2 * d; + Double ml = 22639.6 * FNs(l) - 4586.4 * FNs(l - y); + ml = ml + 2369.9 * FNs(y) + 769 * FNs(2 * l) - 669 * FNs(l1); + ml = ml - 411.6 * FNs(2 * f) - 212 * FNs(2 * l - y); + ml = ml - 206 * FNs(l + l1 - y) + 192 * FNs(l + y); + ml = ml - 165 * FNs(l1 - y) + 148 * FNs(l - l1) - 125 * FNs(d); + ml = ml - 110 * FNs(l + l1) - 55 * FNs(2 * f - y); + ml = ml - 45 * FNs(l + 2 * f) + 40 * FNs(l - 2 * f); + //Double tn = n + 5392 * FNs(2 * f - y) - 541 * FNs(l1) - 442 * FNs(y); + //tn = tn + 423 * FNs(2 * f) - 291 * FNs(2 * l - 2 * f); + g = FNu(FNp(ll + ml)); + Double sign = Math.Floor(g / 30); + //Double degree = g - sign * 30; + sign += 1; - switch (sign.ToString()) - { - case "1": c.AstrologicalSigns.MoonSign = "Aries"; break; - case "2": c.AstrologicalSigns.MoonSign = "Taurus"; break; - case "3": c.AstrologicalSigns.MoonSign = "Gemini"; break; - case "4": c.AstrologicalSigns.MoonSign = "Cancer"; break; - case "5": c.AstrologicalSigns.MoonSign = "Leo"; break; - case "6": c.AstrologicalSigns.MoonSign = "Virgo"; break; - case "7": c.AstrologicalSigns.MoonSign = "Libra"; break; - case "8": c.AstrologicalSigns.MoonSign = "Scorpio"; break; - case "9": c.AstrologicalSigns.MoonSign = "Sagitarius"; break; - case "10": c.AstrologicalSigns.MoonSign = "Capricorn"; break; - case "11": c.AstrologicalSigns.MoonSign = "Aquarius"; break; - case "12": c.AstrologicalSigns.MoonSign = "Pisces"; break; - default: c.AstrologicalSigns.MoonSign = "Pisces"; break; - } + c.AstrologicalSigns.MoonSign = (sign.ToString()) switch + { + "1" => "Aries", + "2" => "Taurus", + "3" => "Gemini", + "4" => "Cancer", + "5" => "Leo", + "6" => "Virgo", + "7" => "Libra", + "8" => "Scorpio", + "9" => "Sagitarius", + "10" => "Capricorn", + "11" => "Aquarius", + "12" => "Pisces", + _ => "Pisces", + }; + } - } + private static Double FNp(Double x) { + Double sgn = x < 0 ? -1 : 1; + return sgn * (Math.Abs(x) / 3600 / 360 - Math.Floor(Math.Abs(x) / 3600.0 / 360.0)) * 360; + } + private static Double FNu(Double x) => x - Math.Floor(x / 360) * 360; + //private static Double FNr(Double x) => Math.PI / 180 * x; + private static Double FNs(Double x) => Math.Sin(Math.PI / 180 * x); - private static double FNp(double x) - { - double sgn; - if (x < 0) - { sgn = -1; } - else - { sgn = 1; } - return sgn * ((Math.Abs(x) / 3600) / 360 - Math.Floor((Math.Abs(x) / 3600.0) / 360.0)) * 360; - } - private static double FNu(double x) - { return x - (Math.Floor(x / 360) * 360); } - private static double FNr(double x) - { return Math.PI / 180 * x; } - private static double FNs(double x) - { return Math.Sin(Math.PI / 180 * x); } + //v1.1.3 Formulas + //The following formulas are either additions + //or conversions of SunCalcs formulas into Meeus - //v1.1.3 Formulas - //The following formulas are either additions - //or conversions of SunCalcs formulas into Meeus + /// + /// Grabs Perigee or Apogee of Moon based on specified time. + /// Results will return event just before, or just after specified DateTime + /// + /// DateTime + /// Event Type + /// PerigeeApogee + private static PerigeeApogee MoonPerigeeOrApogee(DateTime d, MoonDistanceType md) { + //Perigee & Apogee Algorithms from Jean Meeus Astronomical Algorithms Ch. 50 - /// - /// Grabs Perigee or Apogee of Moon based on specified time. - /// Results will return event just before, or just after specified DateTime - /// - /// DateTime - /// Event Type - /// PerigeeApogee - private static PerigeeApogee MoonPerigeeOrApogee(DateTime d, MoonDistanceType md) - { - //Perigee & Apogee Algorithms from Jean Meeus Astronomical Algorithms Ch. 50 + //50.1 + //JDE = 2451534.6698 + 27.55454989 * k + // -0.0006691 * Math.Pow(T,2) + // -0.000.01098 * Math.Pow(T,3) + // -0.0000000052 * Math.Pow(T,4) - //50.1 - //JDE = 2451534.6698 + 27.55454989 * k - // -0.0006691 * Math.Pow(T,2) - // -0.000.01098 * Math.Pow(T,3) - // -0.0000000052 * Math.Pow(T,4) + //50.2 + //K approx = (yv - 1999.97)*13.2555 + //yv is the year + percentage of days that have occured in the year. 1998 Oct 1 is approx 1998.75 + //k ending in .0 represent perigee and .5 apogee. Anything > .5 is an error. - //50.2 - //K approx = (yv - 1999.97)*13.2555 - //yv is the year + percentage of days that have occured in the year. 1998 Oct 1 is approx 1998.75 - //k ending in .0 represent perigee and .5 apogee. Anything > .5 is an error. + //50.3 + //T = k/1325.55 - //50.3 - //T = k/1325.55 + Double yt = 365; //days in year + if (DateTime.IsLeapYear(d.Year)) { yt = 366; } //days in year if leap year + Double f = d.DayOfYear / yt; //Get percentage of year that as passed + Double yv = d.Year + f; //add percentage of year passed to year. + Double k = (yv - 1999.97) * 13.2555; //find approximate k using formula 50.2 - double yt = 365; //days in year - if (DateTime.IsLeapYear(d.Year)) { yt = 366; } //days in year if leap year - double f = d.DayOfYear / yt; //Get percentage of year that as passed - double yv = d.Year + f; //add percentage of year passed to year. - double k = (yv - 1999.97) * 13.2555; //find approximate k using formula 50.2 + //Set k decimal based on apogee or perigee + k = md == MoonDistanceType.Apogee ? Math.Floor(k) + .5 : Math.Floor(k); - //Set k decimal based on apogee or perigee - if (md == MoonDistanceType.Apogee) - { - k = Math.Floor(k) + .5; - } - else - { - k = Math.Floor(k); - } - - //Find T using formula 50.3 - double T = k / 1325.55; - //Find JDE using formula 50.1 - double JDE = 2451534.6698 + 27.55454989 * k - + //Find T using formula 50.3 + Double T = k / 1325.55; + //Find JDE using formula 50.1 + Double JDE = 2451534.6698 + 27.55454989 * k - 0.0006691 * Math.Pow(T, 2) - 0.00001098 * Math.Pow(T, 3) - 0.0000000052 * Math.Pow(T, 4); - //Find Moon's mean elongation at time JDE. - double D = 171.9179 + 335.9106046 * k - + //Find Moon's mean elongation at time JDE. + Double D = 171.9179 + 335.9106046 * k - 0.0100383 * Math.Pow(T, 2) - 0.00001156 * Math.Pow(T, 3) + 0.000000055 * Math.Pow(T, 4); - //Find Sun's mean anomaly at time JDE - double M = 347.3477 + 27.1577721 * k - + //Find Sun's mean anomaly at time JDE + Double M = 347.3477 + 27.1577721 * k - 0.0008130 * Math.Pow(T, 2) - 0.0000010 * Math.Pow(T, 3); - //Find Moon's argument of latitude at Time JDE - double F = 316.6109 + 364.5287911 * k - + //Find Moon's argument of latitude at Time JDE + Double F = 316.6109 + 364.5287911 * k - 0.0125053 * Math.Pow(T, 2) - 0.0000148 * Math.Pow(T, 3); - //Normalize DMF to a 0-360 degree number - D %= 360; - if (D < 0) { D += 360; } - M %= 360; - if (M < 0) { M += 360; } - F %= 360; - if (F < 0) { F += 360; } + //Normalize DMF to a 0-360 degree number + D %= 360; + if (D < 0) { D += 360; } + M %= 360; + if (M < 0) { M += 360; } + F %= 360; + if (F < 0) { F += 360; } - //Convert DMF to radians - D = D * Math.PI / 180; - M = M * Math.PI / 180; - F = F * Math.PI / 180; - double termsA; - //Find Terms A from Table 50.A - if (md == MoonDistanceType.Apogee) - { - termsA = MeeusTables.ApogeeTermsA(D, M, F, T); - } - else - { - termsA = MeeusTables.PerigeeTermsA(D, M, F, T); - } - JDE += termsA; - double termsB; - if (md == MoonDistanceType.Apogee) - { - termsB = MeeusTables.ApogeeTermsB(D, M, F, T); - } - else - { - termsB = MeeusTables.PerigeeTermsB(D, M, F, T); - } - //Convert julian back to date - DateTime date = JulianConversions.GetDate_FromJulian(JDE).Value; - //Obtain distance - Distance dist = GetMoonDistance(date); + //Convert DMF to radians + D = D * Math.PI / 180; + M = M * Math.PI / 180; + F = F * Math.PI / 180; + Double termsA = md == MoonDistanceType.Apogee ? MeeusTables.ApogeeTermsA(D, M, F, T) : MeeusTables.PerigeeTermsA(D, M, F, T); + //Find Terms A from Table 50.A + JDE += termsA; + Double termsB = md == MoonDistanceType.Apogee ? MeeusTables.ApogeeTermsB(D, M, F, T) : MeeusTables.PerigeeTermsB(D, M, F, T); + //Convert julian back to date + DateTime date = JulianConversions.GetDate_FromJulian(JDE).Value; + //Obtain distance + Distance dist = GetMoonDistance(date); - PerigeeApogee ap = new PerigeeApogee(date, termsB, dist); - return ap; + PerigeeApogee ap = new PerigeeApogee(date, termsB, dist); + return ap; + } + + public static Perigee GetPerigeeEvents(DateTime d) { + //Iterate in 15 day increments due to formula variations. + //Determine closest events to date. + //per1 is last date + //per2 is next date + + //integrity for new date. + if (d.Year <= 0001) { return new Perigee(new PerigeeApogee(new DateTime(), 0, new Distance(0)), new PerigeeApogee(new DateTime(), 0, new Distance(0))); } + //Start at lowest increment + PerigeeApogee per1 = MoonPerigeeOrApogee(d.AddDays(-45), MoonDistanceType.Perigee); + PerigeeApogee per2 = MoonPerigeeOrApogee(d.AddDays(-45), MoonDistanceType.Perigee); + + for (Int32 x = -30; x <= 45; x += 15) { + //used for comparison + PerigeeApogee t = MoonPerigeeOrApogee(d.AddDays(x), MoonDistanceType.Perigee); + + //Find the next pergiee after specified date + if (t.Date > per2.Date && t.Date >= d) { + per2 = t; + break; + } + //Find last perigee before specified date + if (t.Date > per1.Date && t.Date < d) { + per1 = t; + per2 = t; } - public static Perigee GetPerigeeEvents(DateTime d) - { - //Iterate in 15 day increments due to formula variations. - //Determine closest events to date. - //per1 is last date - //per2 is next date + } + return new Perigee(per1, per2); + } + public static Apogee GetApogeeEvents(DateTime d) { + //Iterate in 5 month increments due to formula variations. + //Determine closest events to date. + //apo1 is last date + //apo2 is next date - //integrity for new date. - if (d.Year <= 0001) { return new Perigee(new PerigeeApogee(new DateTime(), 0, new Distance(0)), new PerigeeApogee(new DateTime(), 0, new Distance(0))); } - //Start at lowest increment - PerigeeApogee per1 = MoonPerigeeOrApogee(d.AddDays(-45), MoonDistanceType.Perigee); - PerigeeApogee per2 = MoonPerigeeOrApogee(d.AddDays(-45), MoonDistanceType.Perigee); + //integrity for new date. + if (d.Year <= 0001) { return new Apogee(new PerigeeApogee(new DateTime(), 0, new Distance(0)), new PerigeeApogee(new DateTime(), 0, new Distance(0))); } - for (int x = -30; x <= 45; x+=15) - { - //used for comparison - PerigeeApogee t = MoonPerigeeOrApogee(d.AddDays(x), MoonDistanceType.Perigee); - - //Find the next pergiee after specified date - if (t.Date > per2.Date && t.Date >= d) - { - per2 = t; - break; - } - //Find last perigee before specified date - if (t.Date > per1.Date && t.Date < d) - { - per1 = t; - per2 = t; - } - - } - return new Perigee(per1, per2); + PerigeeApogee apo1 = MoonPerigeeOrApogee(d.AddDays(-45), MoonDistanceType.Apogee); + PerigeeApogee apo2 = MoonPerigeeOrApogee(d.AddDays(-45), MoonDistanceType.Apogee); + for (Int32 x = -30; x <= 45; x += 15) { + PerigeeApogee t = MoonPerigeeOrApogee(d.AddDays(x), MoonDistanceType.Apogee); + //Find next apogee after specified date + if (t.Date > apo2.Date && t.Date >= d) { + apo2 = t; + break; } - public static Apogee GetApogeeEvents(DateTime d) - { - //Iterate in 5 month increments due to formula variations. - //Determine closest events to date. - //apo1 is last date - //apo2 is next date - - //integrity for new date. - if (d.Year <= 0001) { return new Apogee(new PerigeeApogee(new DateTime(), 0, new Distance(0)), new PerigeeApogee(new DateTime(), 0, new Distance(0))); } - - PerigeeApogee apo1 = MoonPerigeeOrApogee(d.AddDays(-45), MoonDistanceType.Apogee); - PerigeeApogee apo2 = MoonPerigeeOrApogee(d.AddDays(-45), MoonDistanceType.Apogee); - for (int x = -30; x <= 45; x+=15) - { - PerigeeApogee t = MoonPerigeeOrApogee(d.AddDays(x), MoonDistanceType.Apogee); - //Find next apogee after specified date - if (t.Date > apo2.Date && t.Date >= d) - { - apo2 = t; - break; - } - //Find last apogee before specified date - if (t.Date > apo1.Date && t.Date < d) - { - apo1 = t; - apo2 = t; - } - - } - return new Apogee(apo1, apo2); - + //Find last apogee before specified date + if (t.Date > apo1.Date && t.Date < d) { + apo1 = t; + apo2 = t; } - /// - /// Gets moon distance (Ch 47). - /// - /// DateTime - /// Distance - public static Distance GetMoonDistance(DateTime d) - { - //Ch 47 - double JDE = JulianConversions.GetJulian(d);//Get julian - double T = (JDE - 2451545) / 36525; //Get dynamic time. + } + return new Apogee(apo1, apo2); - double[] values = Get_Moon_LDMNF(T); + } - double D = values[1]; - double M = values[2]; - double N = values[3]; - double F = values[4]; + /// + /// Gets moon distance (Ch 47). + /// + /// DateTime + /// Distance + public static Distance GetMoonDistance(DateTime d) { + //Ch 47 + Double JDE = JulianConversions.GetJulian(d);//Get julian + Double T = (JDE - 2451545) / 36525; //Get dynamic time. - //Ch 47 distance formula - double dist = 385000.56 + (MeeusTables.Moon_Periodic_Er(D, M, N, F, T) / 1000); - return new Distance(dist); - } + Double[] values = Get_Moon_LDMNF(T); - private static Distance GetMoonDistance(DateTime d, double[] values) - { - //Ch 47 - double JDE = JulianConversions.GetJulian(d);//Get julian - double T = (JDE - 2451545) / 36525; //Get dynamic time. + Double D = values[1]; + Double M = values[2]; + Double N = values[3]; + Double F = values[4]; - double D = values[1]; - double M = values[2]; - double N = values[3]; - double F = values[4]; + //Ch 47 distance formula + Double dist = 385000.56 + MeeusTables.Moon_Periodic_Er(D, M, N, F, T) / 1000; + return new Distance(dist); + } - double dist = 385000.56 + (MeeusTables.Moon_Periodic_Er(D, M, N, F, T) / 1000); - return new Distance(dist); - } + /*private static Distance GetMoonDistance(DateTime d, Double[] values) { + //Ch 47 + Double JDE = JulianConversions.GetJulian(d);//Get julian + Double T = (JDE - 2451545) / 36525; //Get dynamic time. - /// - /// Gets Moon L, D, M, N, F values - /// Ch. 47 - /// - /// Dynamic Time - /// double[] containing L,D,M,N,F - static double[] Get_Moon_LDMNF(double T) - { - //T = dynamic time + Double D = values[1]; + Double M = values[2]; + Double N = values[3]; + Double F = values[4]; - //Moon's mean longitude - double L = 218.316447 + 481267.88123421 * T - + Double dist = 385000.56 + MeeusTables.Moon_Periodic_Er(D, M, N, F, T) / 1000; + return new Distance(dist); + }*/ + + /// + /// Gets Moon L, D, M, N, F values + /// Ch. 47 + /// + /// Dynamic Time + /// double[] containing L,D,M,N,F + static Double[] Get_Moon_LDMNF(Double T) { + //T = dynamic time + + //Moon's mean longitude + Double L = 218.316447 + 481267.88123421 * T - .0015786 * Math.Pow(T, 2) + Math.Pow(T, 3) / 538841 - Math.Pow(T, 4) / 65194000; - //Moon's mean elongation - double D = 297.8501921 + 445267.1114034 * T - + //Moon's mean elongation + Double D = 297.8501921 + 445267.1114034 * T - 0.0018819 * Math.Pow(T, 2) + Math.Pow(T, 3) / 545868 - Math.Pow(T, 4) / 113065000; - //Sun's mean anomaly - double M = 357.5291092 + 35999.0502909 * T - + //Sun's mean anomaly + Double M = 357.5291092 + 35999.0502909 * T - .0001536 * Math.Pow(T, 2) + Math.Pow(T, 3) / 24490000; - //Moon's mean anomaly - double N = 134.9633964 + 477198.8675055 * T + .0087414 * Math.Pow(T, 2) + + //Moon's mean anomaly + Double N = 134.9633964 + 477198.8675055 * T + .0087414 * Math.Pow(T, 2) + Math.Pow(T, 3) / 69699 - Math.Pow(T, 4) / 14712000; - //Moon's argument of latitude - double F = 93.2720950 + 483202.0175233 * T - .0036539 * Math.Pow(T, 2) - Math.Pow(T, 3) / + //Moon's argument of latitude + Double F = 93.2720950 + 483202.0175233 * T - .0036539 * Math.Pow(T, 2) - Math.Pow(T, 3) / 3526000 + Math.Pow(T, 4) / 863310000; - //Normalize DMF to a 0-360 degree number - D %= 360; - if (D < 0) { D += 360; } - M %= 360; - if (M < 0) { M += 360; } - N %= 360; - if (N < 0) { N += 360; } - F %= 360; - if (F < 0) { F += 360; } - - //Convert DMF to radians - - D = D * Math.PI / 180; - M = M * Math.PI / 180; - N = N * Math.PI / 180; - F = F * Math.PI / 180; + //Normalize DMF to a 0-360 degree number + D %= 360; + if (D < 0) { D += 360; } + M %= 360; + if (M < 0) { M += 360; } + N %= 360; + if (N < 0) { N += 360; } + F %= 360; + if (F < 0) { F += 360; } - return new double[] { L, D, M, N, F }; - } - /// - /// Get moons lat/long in radians (Ch 47). - /// - /// L,D,M,N,F - /// Dynamic Time - /// Lat[0], Long[1] - private static double[] Get_Moon_Coordinates(double[] LDMNF,double T) - { - //Refence Ch 47. - double lat = LDMNF[0] + (MeeusTables.Moon_Periodic_El(LDMNF[0], LDMNF[1], LDMNF[2], LDMNF[3], LDMNF[4],T)/1000000); - double longi = MeeusTables.Moon_Periodic_Eb(LDMNF[0], LDMNF[1], LDMNF[2], LDMNF[3], LDMNF[4], T) / 1000000; - lat %= 360; - if (lat < 0) { lat += 360; } - - //Convert to radians - double l = rad * lat; // longitude - double b = rad * longi; // latitude - - return new double[] { l, b }; - } - - /// - /// Gets right Ascension of celestial object (Ch 13 Fig 13.3) - /// - /// latitude in radians - /// longitude in radian - /// Right Ascension - private static double rightAscension(double l, double b) - { - //Ch 13 Fig 13.3 - //tan a = ( sin(l) * cos(e) - tan(b)-sin(e) ) / cons(l) - //Converts to the following using Atan2 for 4 quadriatic regions - return Math.Atan2(Math.Sin(l) * Math.Cos(e) - Math.Tan(b) * Math.Sin(e), Math.Cos(l)); - } - /// - /// Gets declination of celestial object (Ch 13 Fig 13.4) - /// - /// latitude in radians - /// longitude in radian - /// Declination - private static double declination(double l, double b) - { - //Ch 13 Fig 13.4 - //sin o = sin(b) * cos(e) + cos(b)*sin(e) * sin(l) - //Converts to the following using Asin - return Math.Asin(Math.Sin(b) * Math.Cos(e) + Math.Cos(b) * Math.Sin(e) * Math.Sin(l)); - } - - static double Parallax_Dec(double distance, double H, double pCosE, double pSinE, double dec, double cRA) - { - //Ch 40 (Correction for parallax - //H - geocentric hour angle of the body (sidereal) IAW Ch 12 - double pi = Math.Asin((Math.Sin(8.794 / distance))) * Math.PI / 180; // 40.1 in radians - H = H * Math.PI / 180; - //Directly to topocencric dec - double tDEC = Math.Atan2((Math.Sin(dec) - pSinE * Math.Sin(pi)) * Math.Cos(cRA), Math.Cos(dec) - pCosE * Math.Sin(pi) * Math.Cos(H)); - return tDEC; + //Convert DMF to radians - } - static double Parallax_RA(double distance, double H, double pCosE, double dec, double ra) - { - //ENSURE RADIANS + D = D * Math.PI / 180; + M = M * Math.PI / 180; + N = N * Math.PI / 180; + F = F * Math.PI / 180; - //Ch 40 (Correction for parallax - //H - geocentric hour angle of the body (sidereal) IAW Ch 12 - - double pi = Math.Asin((Math.Sin(8.794 / distance))) * Math.PI / 180; // 40.1 - - - //Convert to Radian - double t = -pCosE * Math.Sin(pi) * Math.Sin(H); - double b = Math.Cos(dec) - pCosE * Math.Sin(pi) * Math.Cos(H); - double cRA = Math.Atan2(t, b); - return cRA; - //Topocencric RA = RA - cRA - } - static double Get_pSinE(double dec, double H) - { - //ASSUME WGS 84 FOR NOW - double a = 6378.14; - double f = 1 / 298.257; - double b = a * (1 - f); - double ba = .99664719; // or 1-f - double u = (ba * dec) * Math.PI / 180; - - double ps = ba * Math.Sin(u) + (H / 6378140) * Math.Sin(dec); - return ps; - - } - static double Get_pCosE(double dec, double H) - { - //ASSUME WGS 84 FOR NOW - double a = 6378.14; - double f = 1 / 298.257; - double b = a * (1 - f); - double ba = .99664719; // or 1-f - double u = (ba * dec) * Math.PI / 180; - - double ps = Math.Cos(u) + (H / 6378140) * Math.Cos(dec); - return ps; - } - - static double azimuth(double H, double phi, double dec) { return Math.Atan2(Math.Sin(H), Math.Cos(H) * Math.Sin(phi) - Math.Tan(dec) * Math.Cos(phi)); } - static double altitude(double H, double phi, double dec) - { - return Math.Asin(Math.Sin(phi) * Math.Sin(dec) + Math.Cos(phi) * Math.Cos(dec) * Math.Cos(H)); - } - static double astroRefraction(double h) - { - //CH 16 - double P = 1013.25; //Average pressure of earth - double T = 16; //Average temp of earth - double alt = h / Math.PI * 180; - double Ref = P * (.1594 + .0196 * alt + .00002 * Math.Pow(alt, 2)) / ((273 + T) * (1 + .505 * alt + .0845 * Math.Pow(alt, 2))); - return Ref / 60; - } + return new Double[] { L, D, M, N, F }; } + /// + /// Get moons lat/long in radians (Ch 47). + /// + /// L,D,M,N,F + /// Dynamic Time + /// Lat[0], Long[1] + private static Double[] Get_Moon_Coordinates(Double[] LDMNF, Double T) { + //Refence Ch 47. + Double lat = LDMNF[0] + MeeusTables.Moon_Periodic_El(LDMNF[0], LDMNF[1], LDMNF[2], LDMNF[3], LDMNF[4], T) / 1000000; + Double longi = MeeusTables.Moon_Periodic_Eb(LDMNF[0], LDMNF[1], LDMNF[2], LDMNF[3], LDMNF[4], T) / 1000000; + lat %= 360; + if (lat < 0) { lat += 360; } + + //Convert to radians + Double l = rad * lat; // longitude + Double b = rad * longi; // latitude + + return new Double[] { l, b }; + } + + /// + /// Gets right Ascension of celestial object (Ch 13 Fig 13.3) + /// + /// latitude in radians + /// longitude in radian + /// Right Ascension + private static Double RightAscension(Double l, Double b) => + //Ch 13 Fig 13.3 + //tan a = ( sin(l) * cos(e) - tan(b)-sin(e) ) / cons(l) + //Converts to the following using Atan2 for 4 quadriatic regions + Math.Atan2(Math.Sin(l) * Math.Cos(e) - Math.Tan(b) * Math.Sin(e), Math.Cos(l)); + /// + /// Gets declination of celestial object (Ch 13 Fig 13.4) + /// + /// latitude in radians + /// longitude in radian + /// Declination + private static Double Declination(Double l, Double b) => + //Ch 13 Fig 13.4 + //sin o = sin(b) * cos(e) + cos(b)*sin(e) * sin(l) + //Converts to the following using Asin + Math.Asin(Math.Sin(b) * Math.Cos(e) + Math.Cos(b) * Math.Sin(e) * Math.Sin(l)); + + static Double Parallax_Dec(Double distance, Double H, Double pCosE, Double pSinE, Double dec, Double cRA) { + //Ch 40 (Correction for parallax + //H - geocentric hour angle of the body (sidereal) IAW Ch 12 + Double pi = Math.Asin(Math.Sin(8.794 / distance)) * Math.PI / 180; // 40.1 in radians + H = H * Math.PI / 180; + //Directly to topocencric dec + Double tDEC = Math.Atan2((Math.Sin(dec) - pSinE * Math.Sin(pi)) * Math.Cos(cRA), Math.Cos(dec) - pCosE * Math.Sin(pi) * Math.Cos(H)); + return tDEC; + + } + static Double Parallax_RA(Double distance, Double H, Double pCosE, Double dec, Double _) { + //ENSURE RADIANS + + //Ch 40 (Correction for parallax + //H - geocentric hour angle of the body (sidereal) IAW Ch 12 + + Double pi = Math.Asin(Math.Sin(8.794 / distance)) * Math.PI / 180; // 40.1 + + + //Convert to Radian + Double t = -pCosE * Math.Sin(pi) * Math.Sin(H); + Double b = Math.Cos(dec) - pCosE * Math.Sin(pi) * Math.Cos(H); + Double cRA = Math.Atan2(t, b); + return cRA; + //Topocencric RA = RA - cRA + } + static Double Get_pSinE(Double dec, Double H) { + //ASSUME WGS 84 FOR NOW + //Double a = 6378.14; + //Double f = 1 / 298.257; + //Double b = a * (1 - f); + Double ba = .99664719; // or 1-f + Double u = ba * dec * Math.PI / 180; + + Double ps = ba * Math.Sin(u) + H / 6378140 * Math.Sin(dec); + return ps; + + } + static Double Get_pCosE(Double dec, Double H) { + //ASSUME WGS 84 FOR NOW + //Double a = 6378.14; + //Double f = 1 / 298.257; + //Double b = a * (1 - f); + Double ba = .99664719; // or 1-f + Double u = ba * dec * Math.PI / 180; + + Double ps = Math.Cos(u) + H / 6378140 * Math.Cos(dec); + return ps; + } + + static Double Azimuth(Double H, Double phi, Double dec) => Math.Atan2(Math.Sin(H), Math.Cos(H) * Math.Sin(phi) - Math.Tan(dec) * Math.Cos(phi)); + static Double Altitude(Double H, Double phi, Double dec) => Math.Asin(Math.Sin(phi) * Math.Sin(dec) + Math.Cos(phi) * Math.Cos(dec) * Math.Cos(H)); + static Double AstroRefraction(Double h) { + //CH 16 + Double P = 1013.25; //Average pressure of earth + Double T = 16; //Average temp of earth + Double alt = h / Math.PI * 180; + Double Ref = P * (.1594 + .0196 * alt + .00002 * Math.Pow(alt, 2)) / ((273 + T) * (1 + .505 * alt + .0845 * Math.Pow(alt, 2))); + return Ref / 60; + } + } } diff --git a/CoordinateSharp/Celestial.SolarEclipseCalc.cs b/CoordinateSharp/Celestial.SolarEclipseCalc.cs index b4b7c3b..43f7da9 100644 --- a/CoordinateSharp/Celestial.SolarEclipseCalc.cs +++ b/CoordinateSharp/Celestial.SolarEclipseCalc.cs @@ -1,971 +1,735 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -namespace CoordinateSharp -{ - //CURRENT ALTITUDE IS SET CONSTANT AT 100M. POSSIBLY NEED TO ADJUST TO ALLOW USER PASS. - //Altitude adjustments appear to have minimal effect on eclipse timing. These were mainly used - //to signify eclipses that had already started during rise and set times on the NASA calculator +namespace CoordinateSharp { + //CURRENT ALTITUDE IS SET CONSTANT AT 100M. POSSIBLY NEED TO ADJUST TO ALLOW USER PASS. + //Altitude adjustments appear to have minimal effect on eclipse timing. These were mainly used + //to signify eclipses that had already started during rise and set times on the NASA calculator - //SOME TIMES AND ALTS WERE RETURNED WITH COLOR AND STYLING. DETERMINE WHY AND ADJUST VALUE AS REQUIRED. SEARCH "WAS ITALIC". + //SOME TIMES AND ALTS WERE RETURNED WITH COLOR AND STYLING. DETERMINE WHY AND ADJUST VALUE AS REQUIRED. SEARCH "WAS ITALIC". - //ELLIPSOID ADJUSTMENT - //6378140.0 Ellipsoid is used in the NASA Calculator - //WGS84 Ellipsoid is 6378137.0. Adjustments to the ellipsoid appear to effect eclipse seconds in fractions. - //This can be modified if need to allow users to pass custom number with the Coordinate SetDatum() functions. + //ELLIPSOID ADJUSTMENT + //6378140.0 Ellipsoid is used in the NASA Calculator + //WGS84 Ellipsoid is 6378137.0. Adjustments to the ellipsoid appear to effect eclipse seconds in fractions. + //This can be modified if need to allow users to pass custom number with the Coordinate SetDatum() functions. - //CURRENT RANGE 1601-2600. - internal class SolarEclipseCalc - { - public static List> CalculateSolarEclipse(DateTime d, double latRad, double longRad) - { - return Calculate(d, latRad, longRad, null); - } - public static List CalculateSolarEclipse(DateTime d, double latRad, double longRad, double[] events) - { - List> evs = Calculate(d, latRad, longRad, events); - List deetsList = new List(); - foreach(List ls in evs) - { - SolarEclipseDetails deets = new SolarEclipseDetails(ls); - deetsList.Add(deets); - } - return deetsList; - } - public static List> CalculateSolarEclipse(DateTime d, Coordinate coord) - { - return Calculate(d, coord.Latitude.ToRadians(), coord.Longitude.ToRadians(), null); - } - - private static List> Calculate(DateTime d, double latRad, double longRad, double[] ev) - { - //Declare storage arrays - double[] obsvconst = new double[7]; - double[] mid = new double[41];//Check index to see if array needs to be this size - double[] c1 = new double[41]; - double[] c2 = new double[41]; - double[] c3 = new double[41]; - double[] c4 = new double[41]; - - List> events = new List>(); - double[] el; - if (ev == null) - { - el = Eclipse.SolarData.SolarDateData(d);//Get 100 year solar data; - } - else - { - el = ev; - } - - events = new List>(); - ReadData(latRad, longRad, obsvconst); - for (int i = 0; i < el.Length; i += 28) - { - obsvconst[6] = i; - GetAll(el, obsvconst, mid,c1,c2,c3,c4); - // Is there an event... - if (mid[39] > 0) - { - List values = new List(); - values.Add(GetDate(el, mid, obsvconst)); - if (mid[39] == 1) - { - values.Add("P"); - } - else if (mid[39] == 2) - { - values.Add("A"); - } - else - { - values.Add("T"); - } - - // Partial eclipse start - if (c1[40] == 4) - { - values.Add("-"); - values.Add(" "); - } - else - { - // Partial eclipse start time - values.Add(GetTime(el, c1, obsvconst)); - values.Add(GetAlt(c1)); - } - // Central eclipse time - if ((mid[39] > 1) && (c2[40] != 4)) - { - values.Add(GetTime(el, c2, obsvconst)); - } - else - { - values.Add("-"); - } - - //Mid Time - values.Add(GetTime(el, mid, obsvconst)); - - // Maximum eclipse alt - values.Add(GetAlt(mid)); - - // Maximum eclipse azi - values.Add(GetAzi(mid)); - // Central eclipse ends - if ((mid[39] > 1) && (c3[40] != 4)) - { - values.Add(GetTime(el, c3, obsvconst)); - } - else - { - values.Add("-"); - } - // Partial eclipse ends - if (c4[40] == 4) - { - values.Add("-"); - values.Add(" "); - } - else - { - // Partial eclipse ends - values.Add(GetTime(el, c4, obsvconst)); - - // ... sun alt - values.Add(GetAlt(c4)); - } - // Eclipse magnitude - values.Add(GetMagnitude(mid)); - - - // Coverage - values.Add(GetCoverage(mid)); - - // Central duration - if (mid[39] > 1) - { - values.Add(GetDuration(mid,c2,c3)); - } - else - { - values.Add("-"); - } - - events.Add(values); - } - } - return events; - } - //Populates the obsvcont array - private static void ReadData(double latRad, double longRad, double[] obsvconst) - { - // Get the latitude - obsvconst[0] = latRad; - - //// Get the longitude - obsvconst[1] = -1 * longRad; //PASS REVERSE RADIAN. - - // Get the altitude - obsvconst[2] = 100; //CHANGE TO ALLOW USER TO PASS. - - // Get the time zone - obsvconst[3] = 0; //ALWAYS GMT - - // Get the observer's geocentric position - double tmp = Math.Atan(0.99664719 * Math.Tan(obsvconst[0])); - obsvconst[4] = 0.99664719 * Math.Sin(tmp) + (obsvconst[2] / 6378140.0) * Math.Sin(obsvconst[0]); - obsvconst[5] = Math.Cos(tmp) + (obsvconst[2] / 6378140.0 * Math.Cos(obsvconst[0])); - - } - // Populate the c1, c2, mid, c3 and c4 arrays - private static void GetAll(double[] elements, double[] obsvconst, double[] mid, double[] c1, double[] c2,double[] c3, double[] c4) - { - GetMid(elements, obsvconst, mid); - MidObservational(obsvconst, mid); - if (mid[37] > 0.0) - { - Getc1c4(elements, obsvconst, mid,c1,c2,c3,c4); - if ((mid[36] < mid[29]) || (mid[36] < -mid[29])) - { - Getc2c3(elements, obsvconst, mid,c2,c3); - if (mid[29] < 0.0) - { - mid[39] = 3; // Total eclipse - } - else - { - mid[39] = 2; // Annular eclipse - } - Observational(c1, obsvconst, mid); - Observational(c2, obsvconst, mid); - Observational(c3, obsvconst, mid); - Observational(c4, obsvconst, mid); - c2[36] = 999.9; - c3[36] = 999.9; - // Calculate how much of the eclipse is above the horizon - double pattern = 0; - if (c1[40] == 0) { pattern += 10000; } - if (c2[40] == 0) { pattern += 1000; } - if (mid[40] == 0) { pattern += 100; } - if (c3[40] == 0) { pattern += 10; } - if (c4[40] == 0) { pattern += 1; } - // Now, time to make sure that all my Observational[39] and Observational[40] are OK - if (pattern == 11110) - { - GetSunset(elements, c4, obsvconst); - Observational(c4, obsvconst, mid); - c4[40] = 3; - } - else if (pattern == 11100) - { - GetSunset(elements, c3, obsvconst); - Observational(c3, obsvconst, mid); - c3[40] = 3; - CopyCircumstances(c3, c4); - } - else if (pattern == 11000) - { - c3[40] = 4; - GetSunset(elements, mid, obsvconst); - MidObservational(obsvconst, mid); - mid[40] = 3; - CopyCircumstances(mid, c4); - } - else if (pattern == 10000) - { - mid[39] = 1; - GetSunset(elements, mid, obsvconst); - MidObservational(obsvconst, mid); - mid[40] = 3; - CopyCircumstances(mid, c4); - } - else if (pattern == 1111) - { - GetSunrise(elements, c1, obsvconst); - Observational(c1, obsvconst, mid); - c1[40] = 2; - } - else if (pattern == 111) - { - GetSunrise(elements, c2, obsvconst); - Observational(c2, obsvconst, mid); - c2[40] = 2; - CopyCircumstances(c2, c1); - } - else if (pattern == 11) - { - c2[40] = 4; - GetSunrise(elements, mid, obsvconst); - MidObservational(obsvconst, mid); - mid[40] = 2; - CopyCircumstances(mid, c1); - } - else if (pattern == 1) - { - mid[39] = 1; - GetSunrise(elements, mid, obsvconst); - MidObservational(obsvconst, mid); - mid[40] = 2; - CopyCircumstances(mid, c1); - } - else if (pattern == 0) - { - mid[39] = 0; - } - // There are other patterns, but those are the only ones we're covering! - } - else - { - mid[39] = 1; // Partial eclipse - double pattern = 0; - Observational(c1, obsvconst, mid); - Observational(c4, obsvconst, mid); - if (c1[40] == 0) { pattern += 100; } - if (mid[40] == 0) { pattern += 10; } - if (c4[40] == 0) { pattern += 1; } - if (pattern == 110) - { - GetSunset(elements, c4, obsvconst); - Observational(c4, obsvconst, mid); - c4[40] = 3; - } - else if (pattern == 100) - { - GetSunset(elements, mid, obsvconst); - MidObservational(obsvconst, mid); - mid[40] = 3; - CopyCircumstances(mid, c4); - } - else if (pattern == 11) - { - GetSunrise(elements, c1, obsvconst); - Observational(c1, obsvconst, mid); - c1[40] = 2; - } - else if (pattern == 1) - { - GetSunrise(elements, mid, obsvconst); - MidObservational(obsvconst, mid); - mid[40] = 2; - CopyCircumstances(mid, c1); - } - else if (pattern == 0) - { - mid[39] = 0; - } - // There are other patterns, but those are the only ones we're covering! - } - } - else - { - mid[39] = 0; // No eclipse - } - // Magnitude for total and annular eclipse is moon/sun ratio - if ((mid[39] == 2) || (mid[39] == 3)) - { - mid[37] = mid[38]; - } - } - // Calculate mid eclipse - private static void GetMid(double[] elements, double[] obsvconst, double[] mid) - { - double iter, tmp; - - mid[0] = 0; - mid[1] = 0.0; - iter = 0; - tmp = 1.0; - TimeLocDependent(elements, mid, obsvconst); - while (((tmp > 0.000001) || (tmp < -0.000001)) && (iter < 50)) - { - tmp = (mid[24] * mid[26] + mid[25] * mid[27]) / mid[30]; - mid[1] = mid[1] - tmp; - iter++; - TimeLocDependent(elements, mid, obsvconst); - } - } - // Populate the circumstances array with the time and location dependent circumstances - private static double[] TimeLocDependent(double[] elements, double[] circumstances, double[] obsvconst) - { - double index, type; - - TimeDependent(elements, circumstances, obsvconst); - index = obsvconst[6]; - // Calculate h, sin h, cos h - circumstances[16] = circumstances[7] - obsvconst[1] - (elements[(int)index + 5] / 13713.44); - circumstances[17] = Math.Sin(circumstances[16]); - circumstances[18] = Math.Cos(circumstances[16]); - // Calculate xi - circumstances[19] = obsvconst[5] * circumstances[17]; - // Calculate eta - circumstances[20] = obsvconst[4] * circumstances[6] - obsvconst[5] * circumstances[18] * circumstances[5]; - // Calculate zeta - circumstances[21] = obsvconst[4] * circumstances[5] + obsvconst[5] * circumstances[18] * circumstances[6]; - // Calculate dxi - circumstances[22] = circumstances[13] * obsvconst[5] * circumstances[18]; - // Calculate deta - circumstances[23] = circumstances[13] * circumstances[19] * circumstances[5] - circumstances[21] * circumstances[12]; - // Calculate u - circumstances[24] = circumstances[2] - circumstances[19]; - // Calculate v - circumstances[25] = circumstances[3] - circumstances[20]; - // Calculate a - circumstances[26] = circumstances[10] - circumstances[22]; - // Calculate b - circumstances[27] = circumstances[11] - circumstances[23]; - // Calculate l1' - type = circumstances[0]; - if ((type == -2) || (type == 0) || (type == 2)) - { - circumstances[28] = circumstances[8] - circumstances[21] * elements[26 + (int)index]; - } - // Calculate l2' - if ((type == -1) || (type == 0) || (type == 1)) - { - circumstances[29] = circumstances[9] - circumstances[21] * elements[27 + (int)index]; - } - // Calculate n^2 - circumstances[30] = circumstances[26] * circumstances[26] + circumstances[27] * circumstances[27]; - return circumstances; - } - // Populate the circumstances array with the time-only dependent circumstances (x, y, d, m, ...) - private static double[] TimeDependent(double[] elements, double[] circumstances, double[] obsvconst) - { - double type, t, ans; - - t = circumstances[1]; - int index = (int)obsvconst[6]; - // Calculate x - ans = elements[9 + index] * t + elements[8 + index]; - ans = ans * t + elements[7 + index]; - ans = ans * t + elements[6 + index]; - circumstances[2] = ans; - // Calculate dx - ans = 3.0 * elements[9 + index] * t + 2.0 * elements[8 + index]; - ans = ans * t + elements[7 + index]; - circumstances[10] = ans; - // Calculate y - ans = elements[13 + index] * t + elements[12 + index]; - ans = ans * t + elements[11 + index]; - ans = ans * t + elements[10 + index]; - circumstances[3] = ans; - // Calculate dy - ans = 3.0 * elements[13 + index] * t + 2.0 * elements[12 + index]; - ans = ans * t + elements[11 + index]; - circumstances[11] = ans; - // Calculate d - ans = elements[16 + index] * t + elements[15 + index]; - ans = ans * t + elements[14 + index]; - ans = ans * Math.PI / 180.0; - circumstances[4] = ans; - // sin d and cos d - circumstances[5] = Math.Sin(ans); - circumstances[6] = Math.Cos(ans); - // Calculate dd - ans = 2.0 * elements[16 + index] * t + elements[15 + index]; - ans = ans * Math.PI / 180.0; - circumstances[12] = ans; - // Calculate m - ans = elements[19 + index] * t + elements[18 + index]; - ans = ans * t + elements[17 + index]; - if (ans >= 360.0) - { - ans = ans - 360.0; - } - ans = ans * Math.PI / 180.0; - circumstances[7] = ans; - // Calculate dm - ans = 2.0 * elements[19 + index] * t + elements[18 + index]; - ans = ans * Math.PI / 180.0; - circumstances[13] = ans; - // Calculate l1 and dl1 - type = circumstances[0]; - if ((type == -2) || (type == 0) || (type == 2)) - { - ans = elements[22 + index] * t + elements[21 + index]; - ans = ans * t + elements[20 + index]; - circumstances[8] = ans; - circumstances[14] = 2.0 * elements[22 + index] * t + elements[21 + index]; - } - // Calculate l2 and dl2 - if ((type == -1) || (type == 0) || (type == 1)) - { - ans = elements[25 + index] * t + elements[24 + index]; - ans = ans * t + elements[23 + index]; - circumstances[9] = ans; - circumstances[15] = 2.0 * elements[25 + index] * t + elements[24 + index]; - } - return circumstances; - } - // Get the observational circumstances for mid eclipse - private static void MidObservational(double[] obsvconst, double[] mid) - { - Observational(mid, obsvconst, mid); - // Calculate m, magnitude and moon/sun - mid[36] = Math.Sqrt(mid[24] * mid[24] + mid[25] * mid[25]); - mid[37] = (mid[28] - mid[36]) / (mid[28] + mid[29]); - mid[38] = (mid[28] - mid[29]) / (mid[28] + mid[29]); - } - // Get the observational circumstances - private static void Observational(double[] circumstances, double[] obsvconst, double[] mid) - { - double contacttype, coslat, sinlat; - - // We are looking at an "external" contact UNLESS this is a total eclipse AND we are looking at - // c2 or c3, in which case it is an INTERNAL contact! Note that if we are looking at mid eclipse, - // then we may not have determined the type of eclipse (mid[39]) just yet! - if (circumstances[0] == 0) - { - contacttype = 1.0; - } - else - { - if ((mid[39] == 3) && ((circumstances[0] == -1) || (circumstances[0] == 1))) - { - contacttype = -1.0; - } - else - { - contacttype = 1.0; - } - } - // Calculate p - circumstances[31] = Math.Atan2(contacttype * circumstances[24], contacttype * circumstances[25]); - // Calculate alt - sinlat = Math.Sin(obsvconst[0]); - coslat = Math.Cos(obsvconst[0]); - circumstances[32] = Math.Asin(circumstances[5] * sinlat + circumstances[6] * coslat * circumstances[18]); - // Calculate q - circumstances[33] = Math.Asin(coslat * circumstances[17] / Math.Cos(circumstances[32])); - if (circumstances[20] < 0.0) - { - circumstances[33] = Math.PI - circumstances[33]; - } - // Calculate v - circumstances[34] = circumstances[31] - circumstances[33]; - // Calculate azi - circumstances[35] = Math.Atan2(-1.0 * circumstances[17] * circumstances[6], circumstances[5] * coslat - circumstances[18] * sinlat * circumstances[6]); - // Calculate visibility - if (circumstances[32] > -0.00524) - { - circumstances[40] = 0; - } - else - { - circumstances[40] = 1; - } - } - // Get C1 and C4 data - // Entry conditions - - // 1. The mid array must be populated - // 2. The magnitude at mid eclipse must be > 0.0 - private static void Getc1c4(double[] elements, double[] obsvconst, double[] mid, double[] c1, double[] c2, double[] c3, double[] c4) - { - double tmp, n; - - n = Math.Sqrt(mid[30]); - tmp = mid[26] * mid[25] - mid[24] * mid[27]; - tmp = tmp / n / mid[28]; - tmp = Math.Sqrt(1.0 - tmp * tmp) * mid[28] / n; - c1[0] = -2; - c4[0] = 2; - c1[1] = mid[1] - tmp; - c4[1] = mid[1] + tmp; - c1c4iterate(elements, c1, obsvconst); - c1c4iterate(elements, c4, obsvconst); - } - // Iterate on C1 or C4 - private static double[] c1c4iterate(double[] elements, double[] circumstances, double[] obsvconst) - { - double sign, iter, tmp, n; - - TimeLocDependent(elements, circumstances, obsvconst); - if (circumstances[0] < 0) - { - sign = -1.0; - } - else - { - sign = 1.0; - } - tmp = 1.0; - iter = 0; - while (((tmp > 0.000001) || (tmp < -0.000001)) && (iter < 50)) - { - n = Math.Sqrt(circumstances[30]); - tmp = circumstances[26] * circumstances[25] - circumstances[24] * circumstances[27]; - tmp = tmp / n / circumstances[28]; - tmp = sign * Math.Sqrt(1.0 - tmp * tmp) * circumstances[28] / n; - tmp = (circumstances[24] * circumstances[26] + circumstances[25] * circumstances[27]) / circumstances[30] - tmp; - circumstances[1] = circumstances[1] - tmp; - TimeLocDependent(elements, circumstances, obsvconst); - iter++; - } - return circumstances; - } - // Get C2 and C3 data - // Entry conditions - - // 1. The mid array must be populated - // 2. There must be either a total or annular eclipse at the location! - private static void Getc2c3(double[] elements, double[] obsvconst, double[] mid, double[] c2, double[] c3) - { - double tmp, n; - - n = Math.Sqrt(mid[30]); - tmp = mid[26] * mid[25] - mid[24] * mid[27]; - tmp = tmp / n / mid[29]; - tmp = Math.Sqrt(1.0 - tmp * tmp) * mid[29] / n; - c2[0] = -1; - c3[0] = 1; - if (mid[29] < 0.0) - { - c2[1] = mid[1] + tmp; - c3[1] = mid[1] - tmp; - } - else - { - c2[1] = mid[1] - tmp; - c3[1] = mid[1] + tmp; - } - c2c3iterate(elements, c2, obsvconst, mid); - c2c3iterate(elements, c3, obsvconst, mid); - } - // Iterate on C2 or C3 - private static double[] c2c3iterate(double[] elements, double[] circumstances, double[] obsvconst, double[] mid) - { - double sign, iter, tmp, n; - - TimeLocDependent(elements, circumstances, obsvconst); - if (circumstances[0] < 0) - { - sign = -1.0; - } - else - { - sign = 1.0; - } - if (mid[29] < 0.0) - { - sign = -sign; - } - tmp = 1.0; - iter = 0; - while (((tmp > 0.000001) || (tmp < -0.000001)) && (iter < 50)) - { - n = Math.Sqrt(circumstances[30]); - tmp = circumstances[26] * circumstances[25] - circumstances[24] * circumstances[27]; - tmp = tmp / n / circumstances[29]; - tmp = sign * Math.Sqrt(1.0 - tmp * tmp) * circumstances[29] / n; - tmp = (circumstances[24] * circumstances[26] + circumstances[25] * circumstances[27]) / circumstances[30] - tmp; - circumstances[1] = circumstances[1] - tmp; - TimeLocDependent(elements, circumstances, obsvconst); - iter++; - } - return circumstances; - } - // Get the date of an event - private static string GetDate(double[] elements, double[] circumstances, double[] obsvconst) - { - string[] month = new string[] { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; - double t, jd, a, b, c, d, e, index; - string ans = ""; - index = obsvconst[6]; - // Calculate the JD for noon (TDT) the day before the day that contains T0 - jd = Math.Floor(elements[(int)index] - (elements[1 + (int)index] / 24.0)); - // Calculate the local time (ie the offset in hours since midnight TDT on the day containing T0). - t = circumstances[1] + elements[1 + (int)index] - obsvconst[3] - (elements[4 + (int)index] - 0.5) / 3600.0; - if (t < 0.0) - { - jd--; - } - if (t >= 24.0) - { - jd++; - } - if (jd >= 2299160.0) - { - a = Math.Floor((jd - 1867216.25) / 36524.25); - a = jd + 1 + a - Math.Floor(a / 4.0); - } - else - { - a = jd; - } - b = a + 1525.0; - c = Math.Floor((b - 122.1) / 365.25); - d = Math.Floor(365.25 * c); - e = Math.Floor((b - d) / 30.6001); - d = b - d - Math.Floor(30.6001 * e); - if (e < 13.5) - { - e = e - 1; - } - else - { - e = e - 13; - } - double year; - if (e > 2.5) - { - ans = c - 4716 + "-"; - year = c - 4716; - } - else - { - ans = c - 4715 + "-"; - year = c - 4715; - } - string m = month[(int)e - 1]; - ans += m + "-"; - if (d < 10) - { - ans = ans + "0"; - } - - ans = ans + d; - //Leap Year Integrity Check - - if(m =="Feb" && d ==29 && !DateTime.IsLeapYear((int)year)) - { - ans = year.ToString() + "-Mar-01"; - } - return ans; - } - // Calculate the time of sunset - private static void GetSunset(double[] elements, double[] circumstances, double[] obsvconst) - { - GetSunriset(elements, circumstances, 1.0, obsvconst); - } - // Calculate the time of sunrise - private static void GetSunrise(double[] elements, double[] circumstances, double[] obsvconst) - { - GetSunriset(elements, circumstances, -1.0, obsvconst); - } - // Calculate the time of sunrise or sunset - private static void GetSunriset(double[] elements, double[] circumstances, double riset, double[] obsvconst) - { - double h0, diff, iter; - - diff = 1.0; - iter = 0; - while ((diff > 0.00001) || (diff < -0.00001)) - { - iter++; - if (iter == 4) { return; } - h0 = Math.Acos((Math.Sin(-0.00524) - Math.Sin(obsvconst[0]) * circumstances[5]) / Math.Cos(obsvconst[0]) / circumstances[6]); - diff = (riset * h0 - circumstances[16]) / circumstances[13]; - while (diff >= 12.0) { diff -= 24.0; } - while (diff <= -12.0) { diff += 24.0; } - circumstances[1] += diff; - TimeLocDependent(elements, circumstances, obsvconst); - } - } - // Copy a set of circumstances - private static void CopyCircumstances(double[] circumstancesfrom, double[] circumstancesto) - { - for (int i = 1; i < 41; i++) - { - circumstancesto[i] = circumstancesfrom[i]; - } - } - // Get the local time of an event - private static string GetTime(double[] elements, double[] circumstances, double[] obsvconst) - { - string ans = ""; - int index = (int)obsvconst[6]; - double t = circumstances[1] + elements[1 + index] - obsvconst[3] - (elements[4 + index] - 0.5) / 3600.0; - if (t < 0.0) - { - t = t + 24.0; - } - if (t >= 24.0) - { - t = t - 24.0; - } - if (t < 10.0) - { - ans = ans + "0"; - } - ans = ans + Math.Floor(t) + ":"; - t = (t * 60.0) - 60.0 * Math.Floor(t); - if (t < 10.0) - { - ans = ans + "0"; - } - ans = ans + Math.Floor(t); - if (circumstances[40] <= 1) - { // not sunrise or sunset - ans = ans + ":"; - t = (t * 60.0) - 60.0 * Math.Floor(t); - if (t < 10.0) - { - ans = ans + "0"; - } - ans = ans + Math.Floor(t); - } - if (circumstances[40] == 1) - { - //WAS ITALIC - return ans; - } - else if (circumstances[40] == 2) - { - //Rise (CHANGED FROM NASA CALC THE INDICATES (r) WITH STRING, INVESTIGATE REMOVAL) - return ans; - } - else if (circumstances[40] == 3) - { - //Set (CHANGED FROM NASA CALC THE INDICATES (s) WITH STRING, INVESTIGATE REMOVAL) - return ans; - } - else - { - return ans; - } - } - // Get the altitude - private static string GetAlt(double[] circumstances) - { - double t; - string ans = ""; - - if (circumstances[40] == 2) - { - return "0(r)"; - } - if (circumstances[40] == 3) - { - return "0(s)"; - } - if ((circumstances[32] < 0.0) && (circumstances[32] >= -0.00524)) - { - // Crude correction for refraction (and for consistency's sake) - t = 0.0; - } - else - { - t = circumstances[32] * 180.0 / Math.PI; - } - if (t < 0.0) - { - ans = "-"; - t = -t; - } - else - { - ans = ""; - } - t = Math.Floor(t + 0.5); - if (t < 10.0) - { - ans = ans + "0"; - } - ans = ans + t; - if (circumstances[40] == 1) - { - //WAS ITALIC - return ans; - } - else - { - return ans; - } - } - // Get the azimuth - private static string GetAzi(double[] circumstances) - { - string ans = ""; - double t = circumstances[35] * 180.0 / Math.PI; - if (t < 0.0) - { - t = t + 360.0; - } - if (t >= 360.0) - { - t = t - 360.0; - } - t = Math.Floor(t + 0.5); - if (t < 100.0) - { - ans = ans + "0"; - } - if (t < 10.0) - { - ans = ans + "0"; - } - ans = ans + t; - if (circumstances[40] == 1) - { - //WAS ITALIC - return ans; - } - else - { - return ans; - } - } - // Get the magnitude - private static string GetMagnitude(double[] mid) - { - double a = Math.Floor(1000.0 * mid[37] + 0.5) / 1000.0; - string ans = a.ToString(); - if (mid[40] == 1) - { - return ans; - } - if (mid[40] == 2) - { - ans = a.ToString() + "(r)"; - } - if (mid[40] == 3) - { - ans = a.ToString() + "(s)"; - } - return ans; - } - // Get the coverage - private static string GetCoverage(double[] mid) - { - double a=0, b, c; - string ans = ""; - if (mid[37] <= 0.0) - { - ans = "0.0"; - } - else if (mid[37] >= 1.0) - { - ans = "1.000"; - } - else - { - if (mid[39] == 2) - { - c = mid[38] * mid[38]; - } - else - { - c = Math.Acos((mid[28] * mid[28] + mid[29] * mid[29] - 2.0 * mid[36] * mid[36]) / (mid[28] * mid[28] - mid[29] * mid[29])); - b = Math.Acos((mid[28] * mid[29] + mid[36] * mid[36]) / mid[36] / (mid[28] + mid[29])); - a = Math.PI - b - c; - c = ((mid[38] * mid[38] * a + b) - mid[38] * Math.Sin(c)) / Math.PI; - } - a = Math.Floor(1000.0 * c + 0.5) / 1000.0; - ans = a.ToString(); - } - if (mid[40] == 1) - { - //WAS ITALIC - return ans; - } - if (mid[40] == 2) - { - ans = a.ToString() + "(r)"; - } - if (mid[40] == 3) - { - ans = a + "(s)"; - } - return ans; - } - // Get the duration in mm:ss.s format - // Adapted from code written by Stephen McCann - 27/04/2001 - private static string GetDuration(double[] mid, double[] c2, double[] c3) - { - double tmp; - string ans; - - if (c3[40] == 4) - { - tmp = mid[1] - c2[1]; - } - else if (c2[40] == 4) - { - tmp = c3[1] - mid[1]; - } - else - { - tmp = c3[1] - c2[1]; - } - if (tmp < 0.0) - { - tmp = tmp + 24.0; - } - else if (tmp >= 24.0) - { - tmp = tmp - 24.0; - } - tmp = (tmp * 60.0) - 60.0 * Math.Floor(tmp) + 0.05 / 60.0; - ans = Math.Floor(tmp) + "m"; - tmp = (tmp * 60.0) - 60.0 * Math.Floor(tmp); - if (tmp < 10.0) - { - ans = ans + "0"; - } - ans += Math.Floor(tmp) + "s"; - return ans; - } + //CURRENT RANGE 1601-2600. + internal class SolarEclipseCalc { + public static List> CalculateSolarEclipse(DateTime d, Double latRad, Double longRad) => Calculate(d, latRad, longRad, null); + public static List CalculateSolarEclipse(DateTime d, Double latRad, Double longRad, Double[] events) { + List> evs = Calculate(d, latRad, longRad, events); + List deetsList = new List(); + foreach (List ls in evs) { + SolarEclipseDetails deets = new SolarEclipseDetails(ls); + deetsList.Add(deets); + } + return deetsList; } + public static List> CalculateSolarEclipse(DateTime d, Coordinate coord) => Calculate(d, coord.Latitude.ToRadians(), coord.Longitude.ToRadians(), null); + + private static List> Calculate(DateTime d, Double latRad, Double longRad, Double[] ev) { + //Declare storage arrays + Double[] obsvconst = new Double[7]; + Double[] mid = new Double[41];//Check index to see if array needs to be this size + Double[] c1 = new Double[41]; + Double[] c2 = new Double[41]; + Double[] c3 = new Double[41]; + Double[] c4 = new Double[41]; + Double[] el = ev ?? Eclipse.SolarData.SolarDateData(d); + List> events = new List>(); + ReadData(latRad, longRad, obsvconst); + for (Int32 i = 0; i < el.Length; i += 28) { + obsvconst[6] = i; + GetAll(el, obsvconst, mid, c1, c2, c3, c4); + // Is there an event... + if (mid[39] > 0) { + List values = new List { + GetDate(el, mid, obsvconst) + }; + if (mid[39] == 1) { + values.Add("P"); + } else if (mid[39] == 2) { + values.Add("A"); + } else { + values.Add("T"); + } + + // Partial eclipse start + if (c1[40] == 4) { + values.Add("-"); + values.Add(" "); + } else { + // Partial eclipse start time + values.Add(GetTime(el, c1, obsvconst)); + values.Add(GetAlt(c1)); + } + // Central eclipse time + if (mid[39] > 1 && c2[40] != 4) { + values.Add(GetTime(el, c2, obsvconst)); + } else { + values.Add("-"); + } + + //Mid Time + values.Add(GetTime(el, mid, obsvconst)); + + // Maximum eclipse alt + values.Add(GetAlt(mid)); + + // Maximum eclipse azi + values.Add(GetAzi(mid)); + // Central eclipse ends + if (mid[39] > 1 && c3[40] != 4) { + values.Add(GetTime(el, c3, obsvconst)); + } else { + values.Add("-"); + } + // Partial eclipse ends + if (c4[40] == 4) { + values.Add("-"); + values.Add(" "); + } else { + // Partial eclipse ends + values.Add(GetTime(el, c4, obsvconst)); + + // ... sun alt + values.Add(GetAlt(c4)); + } + // Eclipse magnitude + values.Add(GetMagnitude(mid)); + + + // Coverage + values.Add(GetCoverage(mid)); + + // Central duration + if (mid[39] > 1) { + values.Add(GetDuration(mid, c2, c3)); + } else { + values.Add("-"); + } + + events.Add(values); + } + } + return events; + } + //Populates the obsvcont array + private static void ReadData(Double latRad, Double longRad, Double[] obsvconst) { + // Get the latitude + obsvconst[0] = latRad; + + //// Get the longitude + obsvconst[1] = -1 * longRad; //PASS REVERSE RADIAN. + + // Get the altitude + obsvconst[2] = 100; //CHANGE TO ALLOW USER TO PASS. + + // Get the time zone + obsvconst[3] = 0; //ALWAYS GMT + + // Get the observer's geocentric position + Double tmp = Math.Atan(0.99664719 * Math.Tan(obsvconst[0])); + obsvconst[4] = 0.99664719 * Math.Sin(tmp) + obsvconst[2] / 6378140.0 * Math.Sin(obsvconst[0]); + obsvconst[5] = Math.Cos(tmp) + obsvconst[2] / 6378140.0 * Math.Cos(obsvconst[0]); + + } + // Populate the c1, c2, mid, c3 and c4 arrays + private static void GetAll(Double[] elements, Double[] obsvconst, Double[] mid, Double[] c1, Double[] c2, Double[] c3, Double[] c4) { + GetMid(elements, obsvconst, mid); + MidObservational(obsvconst, mid); + if (mid[37] > 0.0) { + Getc1c4(elements, obsvconst, mid, c1, c2, c3, c4); + if (mid[36] < mid[29] || mid[36] < -mid[29]) { + Getc2c3(elements, obsvconst, mid, c2, c3); + mid[39] = mid[29] < 0.0 ? 3 : 2; + Observational(c1, obsvconst, mid); + Observational(c2, obsvconst, mid); + Observational(c3, obsvconst, mid); + Observational(c4, obsvconst, mid); + c2[36] = 999.9; + c3[36] = 999.9; + // Calculate how much of the eclipse is above the horizon + Double pattern = 0; + if (c1[40] == 0) { pattern += 10000; } + if (c2[40] == 0) { pattern += 1000; } + if (mid[40] == 0) { pattern += 100; } + if (c3[40] == 0) { pattern += 10; } + if (c4[40] == 0) { pattern += 1; } + // Now, time to make sure that all my Observational[39] and Observational[40] are OK + if (pattern == 11110) { + GetSunset(elements, c4, obsvconst); + Observational(c4, obsvconst, mid); + c4[40] = 3; + } else if (pattern == 11100) { + GetSunset(elements, c3, obsvconst); + Observational(c3, obsvconst, mid); + c3[40] = 3; + CopyCircumstances(c3, c4); + } else if (pattern == 11000) { + c3[40] = 4; + GetSunset(elements, mid, obsvconst); + MidObservational(obsvconst, mid); + mid[40] = 3; + CopyCircumstances(mid, c4); + } else if (pattern == 10000) { + mid[39] = 1; + GetSunset(elements, mid, obsvconst); + MidObservational(obsvconst, mid); + mid[40] = 3; + CopyCircumstances(mid, c4); + } else if (pattern == 1111) { + GetSunrise(elements, c1, obsvconst); + Observational(c1, obsvconst, mid); + c1[40] = 2; + } else if (pattern == 111) { + GetSunrise(elements, c2, obsvconst); + Observational(c2, obsvconst, mid); + c2[40] = 2; + CopyCircumstances(c2, c1); + } else if (pattern == 11) { + c2[40] = 4; + GetSunrise(elements, mid, obsvconst); + MidObservational(obsvconst, mid); + mid[40] = 2; + CopyCircumstances(mid, c1); + } else if (pattern == 1) { + mid[39] = 1; + GetSunrise(elements, mid, obsvconst); + MidObservational(obsvconst, mid); + mid[40] = 2; + CopyCircumstances(mid, c1); + } else if (pattern == 0) { + mid[39] = 0; + } + // There are other patterns, but those are the only ones we're covering! + } else { + mid[39] = 1; // Partial eclipse + Double pattern = 0; + Observational(c1, obsvconst, mid); + Observational(c4, obsvconst, mid); + if (c1[40] == 0) { pattern += 100; } + if (mid[40] == 0) { pattern += 10; } + if (c4[40] == 0) { pattern += 1; } + if (pattern == 110) { + GetSunset(elements, c4, obsvconst); + Observational(c4, obsvconst, mid); + c4[40] = 3; + } else if (pattern == 100) { + GetSunset(elements, mid, obsvconst); + MidObservational(obsvconst, mid); + mid[40] = 3; + CopyCircumstances(mid, c4); + } else if (pattern == 11) { + GetSunrise(elements, c1, obsvconst); + Observational(c1, obsvconst, mid); + c1[40] = 2; + } else if (pattern == 1) { + GetSunrise(elements, mid, obsvconst); + MidObservational(obsvconst, mid); + mid[40] = 2; + CopyCircumstances(mid, c1); + } else if (pattern == 0) { + mid[39] = 0; + } + // There are other patterns, but those are the only ones we're covering! + } + } else { + mid[39] = 0; // No eclipse + } + // Magnitude for total and annular eclipse is moon/sun ratio + if (mid[39] == 2 || mid[39] == 3) { + mid[37] = mid[38]; + } + } + // Calculate mid eclipse + private static void GetMid(Double[] elements, Double[] obsvconst, Double[] mid) { + Double iter, tmp; + + mid[0] = 0; + mid[1] = 0.0; + iter = 0; + tmp = 1.0; + _ = TimeLocDependent(elements, mid, obsvconst); + while ((tmp > 0.000001 || tmp < -0.000001) && iter < 50) { + tmp = (mid[24] * mid[26] + mid[25] * mid[27]) / mid[30]; + mid[1] = mid[1] - tmp; + iter++; + _ = TimeLocDependent(elements, mid, obsvconst); + } + } + // Populate the circumstances array with the time and location dependent circumstances + private static Double[] TimeLocDependent(Double[] elements, Double[] circumstances, Double[] obsvconst) { + Double index, type; + + _ = TimeDependent(elements, circumstances, obsvconst); + index = obsvconst[6]; + // Calculate h, sin h, cos h + circumstances[16] = circumstances[7] - obsvconst[1] - elements[(Int32)index + 5] / 13713.44; + circumstances[17] = Math.Sin(circumstances[16]); + circumstances[18] = Math.Cos(circumstances[16]); + // Calculate xi + circumstances[19] = obsvconst[5] * circumstances[17]; + // Calculate eta + circumstances[20] = obsvconst[4] * circumstances[6] - obsvconst[5] * circumstances[18] * circumstances[5]; + // Calculate zeta + circumstances[21] = obsvconst[4] * circumstances[5] + obsvconst[5] * circumstances[18] * circumstances[6]; + // Calculate dxi + circumstances[22] = circumstances[13] * obsvconst[5] * circumstances[18]; + // Calculate deta + circumstances[23] = circumstances[13] * circumstances[19] * circumstances[5] - circumstances[21] * circumstances[12]; + // Calculate u + circumstances[24] = circumstances[2] - circumstances[19]; + // Calculate v + circumstances[25] = circumstances[3] - circumstances[20]; + // Calculate a + circumstances[26] = circumstances[10] - circumstances[22]; + // Calculate b + circumstances[27] = circumstances[11] - circumstances[23]; + // Calculate l1' + type = circumstances[0]; + if (type == -2 || type == 0 || type == 2) { + circumstances[28] = circumstances[8] - circumstances[21] * elements[26 + (Int32)index]; + } + // Calculate l2' + if (type == -1 || type == 0 || type == 1) { + circumstances[29] = circumstances[9] - circumstances[21] * elements[27 + (Int32)index]; + } + // Calculate n^2 + circumstances[30] = circumstances[26] * circumstances[26] + circumstances[27] * circumstances[27]; + return circumstances; + } + // Populate the circumstances array with the time-only dependent circumstances (x, y, d, m, ...) + private static Double[] TimeDependent(Double[] elements, Double[] circumstances, Double[] obsvconst) { + Double type, t, ans; + + t = circumstances[1]; + Int32 index = (Int32)obsvconst[6]; + // Calculate x + ans = elements[9 + index] * t + elements[8 + index]; + ans = ans * t + elements[7 + index]; + ans = ans * t + elements[6 + index]; + circumstances[2] = ans; + // Calculate dx + ans = 3.0 * elements[9 + index] * t + 2.0 * elements[8 + index]; + ans = ans * t + elements[7 + index]; + circumstances[10] = ans; + // Calculate y + ans = elements[13 + index] * t + elements[12 + index]; + ans = ans * t + elements[11 + index]; + ans = ans * t + elements[10 + index]; + circumstances[3] = ans; + // Calculate dy + ans = 3.0 * elements[13 + index] * t + 2.0 * elements[12 + index]; + ans = ans * t + elements[11 + index]; + circumstances[11] = ans; + // Calculate d + ans = elements[16 + index] * t + elements[15 + index]; + ans = ans * t + elements[14 + index]; + ans = ans * Math.PI / 180.0; + circumstances[4] = ans; + // sin d and cos d + circumstances[5] = Math.Sin(ans); + circumstances[6] = Math.Cos(ans); + // Calculate dd + ans = 2.0 * elements[16 + index] * t + elements[15 + index]; + ans = ans * Math.PI / 180.0; + circumstances[12] = ans; + // Calculate m + ans = elements[19 + index] * t + elements[18 + index]; + ans = ans * t + elements[17 + index]; + if (ans >= 360.0) { + ans -= 360.0; + } + ans = ans * Math.PI / 180.0; + circumstances[7] = ans; + // Calculate dm + ans = 2.0 * elements[19 + index] * t + elements[18 + index]; + ans = ans * Math.PI / 180.0; + circumstances[13] = ans; + // Calculate l1 and dl1 + type = circumstances[0]; + if (type == -2 || type == 0 || type == 2) { + ans = elements[22 + index] * t + elements[21 + index]; + ans = ans * t + elements[20 + index]; + circumstances[8] = ans; + circumstances[14] = 2.0 * elements[22 + index] * t + elements[21 + index]; + } + // Calculate l2 and dl2 + if (type == -1 || type == 0 || type == 1) { + ans = elements[25 + index] * t + elements[24 + index]; + ans = ans * t + elements[23 + index]; + circumstances[9] = ans; + circumstances[15] = 2.0 * elements[25 + index] * t + elements[24 + index]; + } + return circumstances; + } + // Get the observational circumstances for mid eclipse + private static void MidObservational(Double[] obsvconst, Double[] mid) { + Observational(mid, obsvconst, mid); + // Calculate m, magnitude and moon/sun + mid[36] = Math.Sqrt(mid[24] * mid[24] + mid[25] * mid[25]); + mid[37] = (mid[28] - mid[36]) / (mid[28] + mid[29]); + mid[38] = (mid[28] - mid[29]) / (mid[28] + mid[29]); + } + // Get the observational circumstances + private static void Observational(Double[] circumstances, Double[] obsvconst, Double[] mid) { + Double contacttype, coslat, sinlat; + + // We are looking at an "external" contact UNLESS this is a total eclipse AND we are looking at + // c2 or c3, in which case it is an INTERNAL contact! Note that if we are looking at mid eclipse, + // then we may not have determined the type of eclipse (mid[39]) just yet! + contacttype = circumstances[0] == 0 ? 1.0 : mid[39] == 3 && (circumstances[0] == -1 || circumstances[0] == 1) ? -1.0 : 1.0; + // Calculate p + circumstances[31] = Math.Atan2(contacttype * circumstances[24], contacttype * circumstances[25]); + // Calculate alt + sinlat = Math.Sin(obsvconst[0]); + coslat = Math.Cos(obsvconst[0]); + circumstances[32] = Math.Asin(circumstances[5] * sinlat + circumstances[6] * coslat * circumstances[18]); + // Calculate q + circumstances[33] = Math.Asin(coslat * circumstances[17] / Math.Cos(circumstances[32])); + if (circumstances[20] < 0.0) { + circumstances[33] = Math.PI - circumstances[33]; + } + // Calculate v + circumstances[34] = circumstances[31] - circumstances[33]; + // Calculate azi + circumstances[35] = Math.Atan2(-1.0 * circumstances[17] * circumstances[6], circumstances[5] * coslat - circumstances[18] * sinlat * circumstances[6]); + // Calculate visibility + circumstances[40] = circumstances[32] > -0.00524 ? 0 : 1; + } + // Get C1 and C4 data + // Entry conditions - + // 1. The mid array must be populated + // 2. The magnitude at mid eclipse must be > 0.0 + private static void Getc1c4(Double[] elements, Double[] obsvconst, Double[] mid, Double[] c1, Double[] _1, Double[] _2, Double[] c4) { + Double tmp, n; + + n = Math.Sqrt(mid[30]); + tmp = mid[26] * mid[25] - mid[24] * mid[27]; + tmp = tmp / n / mid[28]; + tmp = Math.Sqrt(1.0 - tmp * tmp) * mid[28] / n; + c1[0] = -2; + c4[0] = 2; + c1[1] = mid[1] - tmp; + c4[1] = mid[1] + tmp; + _ = C1c4iterate(elements, c1, obsvconst); + _ = C1c4iterate(elements, c4, obsvconst); + } + // Iterate on C1 or C4 + private static Double[] C1c4iterate(Double[] elements, Double[] circumstances, Double[] obsvconst) { + Double sign, iter, tmp, n; + + _ = TimeLocDependent(elements, circumstances, obsvconst); + sign = circumstances[0] < 0 ? -1.0 : 1.0; + tmp = 1.0; + iter = 0; + while ((tmp > 0.000001 || tmp < -0.000001) && iter < 50) { + n = Math.Sqrt(circumstances[30]); + tmp = circumstances[26] * circumstances[25] - circumstances[24] * circumstances[27]; + tmp = tmp / n / circumstances[28]; + tmp = sign * Math.Sqrt(1.0 - tmp * tmp) * circumstances[28] / n; + tmp = (circumstances[24] * circumstances[26] + circumstances[25] * circumstances[27]) / circumstances[30] - tmp; + circumstances[1] = circumstances[1] - tmp; + _ = TimeLocDependent(elements, circumstances, obsvconst); + iter++; + } + return circumstances; + } + // Get C2 and C3 data + // Entry conditions - + // 1. The mid array must be populated + // 2. There must be either a total or annular eclipse at the location! + private static void Getc2c3(Double[] elements, Double[] obsvconst, Double[] mid, Double[] c2, Double[] c3) { + Double tmp, n; + + n = Math.Sqrt(mid[30]); + tmp = mid[26] * mid[25] - mid[24] * mid[27]; + tmp = tmp / n / mid[29]; + tmp = Math.Sqrt(1.0 - tmp * tmp) * mid[29] / n; + c2[0] = -1; + c3[0] = 1; + if (mid[29] < 0.0) { + c2[1] = mid[1] + tmp; + c3[1] = mid[1] - tmp; + } else { + c2[1] = mid[1] - tmp; + c3[1] = mid[1] + tmp; + } + _ = C2c3iterate(elements, c2, obsvconst, mid); + _ = C2c3iterate(elements, c3, obsvconst, mid); + } + // Iterate on C2 or C3 + private static Double[] C2c3iterate(Double[] elements, Double[] circumstances, Double[] obsvconst, Double[] mid) { + Double sign, iter, tmp, n; + + _ = TimeLocDependent(elements, circumstances, obsvconst); + sign = circumstances[0] < 0 ? -1.0 : 1.0; + if (mid[29] < 0.0) { + sign = -sign; + } + tmp = 1.0; + iter = 0; + while ((tmp > 0.000001 || tmp < -0.000001) && iter < 50) { + n = Math.Sqrt(circumstances[30]); + tmp = circumstances[26] * circumstances[25] - circumstances[24] * circumstances[27]; + tmp = tmp / n / circumstances[29]; + tmp = sign * Math.Sqrt(1.0 - tmp * tmp) * circumstances[29] / n; + tmp = (circumstances[24] * circumstances[26] + circumstances[25] * circumstances[27]) / circumstances[30] - tmp; + circumstances[1] = circumstances[1] - tmp; + _ = TimeLocDependent(elements, circumstances, obsvconst); + iter++; + } + return circumstances; + } + // Get the date of an event + private static String GetDate(Double[] elements, Double[] circumstances, Double[] obsvconst) { + String[] month = new String[] { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + Double t, jd, a, b, c, d, e, index; + index = obsvconst[6]; + // Calculate the JD for noon (TDT) the day before the day that contains T0 + jd = Math.Floor(elements[(Int32)index] - elements[1 + (Int32)index] / 24.0); + // Calculate the local time (ie the offset in hours since midnight TDT on the day containing T0). + t = circumstances[1] + elements[1 + (Int32)index] - obsvconst[3] - (elements[4 + (Int32)index] - 0.5) / 3600.0; + if (t < 0.0) { + jd--; + } + if (t >= 24.0) { + jd++; + } + if (jd >= 2299160.0) { + a = Math.Floor((jd - 1867216.25) / 36524.25); + a = jd + 1 + a - Math.Floor(a / 4.0); + } else { + a = jd; + } + b = a + 1525.0; + c = Math.Floor((b - 122.1) / 365.25); + d = Math.Floor(365.25 * c); + e = Math.Floor((b - d) / 30.6001); + d = b - d - Math.Floor(30.6001 * e); + if (e < 13.5) { + e -= 1; + } else { + e -= 13; + } + Double year; + String ans; + if (e > 2.5) { + ans = c - 4716 + "-"; + year = c - 4716; + } else { + ans = c - 4715 + "-"; + year = c - 4715; + } + String m = month[(Int32)e - 1]; + ans += m + "-"; + if (d < 10) { + ans += "0"; + } + + ans += d; + //Leap Year Integrity Check + + if (m == "Feb" && d == 29 && !DateTime.IsLeapYear((Int32)year)) { + ans = year.ToString() + "-Mar-01"; + } + return ans; + } + // Calculate the time of sunset + private static void GetSunset(Double[] elements, Double[] circumstances, Double[] obsvconst) => GetSunriset(elements, circumstances, 1.0, obsvconst); + // Calculate the time of sunrise + private static void GetSunrise(Double[] elements, Double[] circumstances, Double[] obsvconst) => GetSunriset(elements, circumstances, -1.0, obsvconst); + // Calculate the time of sunrise or sunset + private static void GetSunriset(Double[] elements, Double[] circumstances, Double riset, Double[] obsvconst) { + Double h0, diff, iter; + + diff = 1.0; + iter = 0; + while (diff > 0.00001 || diff < -0.00001) { + iter++; + if (iter == 4) { return; } + h0 = Math.Acos((Math.Sin(-0.00524) - Math.Sin(obsvconst[0]) * circumstances[5]) / Math.Cos(obsvconst[0]) / circumstances[6]); + diff = (riset * h0 - circumstances[16]) / circumstances[13]; + while (diff >= 12.0) { diff -= 24.0; } + while (diff <= -12.0) { diff += 24.0; } + circumstances[1] += diff; + _ = TimeLocDependent(elements, circumstances, obsvconst); + } + } + // Copy a set of circumstances + private static void CopyCircumstances(Double[] circumstancesfrom, Double[] circumstancesto) { + for (Int32 i = 1; i < 41; i++) { + circumstancesto[i] = circumstancesfrom[i]; + } + } + // Get the local time of an event + private static String GetTime(Double[] elements, Double[] circumstances, Double[] obsvconst) { + String ans = ""; + Int32 index = (Int32)obsvconst[6]; + Double t = circumstances[1] + elements[1 + index] - obsvconst[3] - (elements[4 + index] - 0.5) / 3600.0; + if (t < 0.0) { + t += 24.0; + } + if (t >= 24.0) { + t -= 24.0; + } + if (t < 10.0) { + ans += "0"; + } + ans = ans + Math.Floor(t) + ":"; + t = t * 60.0 - 60.0 * Math.Floor(t); + if (t < 10.0) { + ans += "0"; + } + ans += Math.Floor(t); + if (circumstances[40] <= 1) { // not sunrise or sunset + ans += ":"; + t = t * 60.0 - 60.0 * Math.Floor(t); + if (t < 10.0) { + ans += "0"; + } + ans += Math.Floor(t); + } + if (circumstances[40] == 1) { + //WAS ITALIC + return ans; + } else if (circumstances[40] == 2) { + //Rise (CHANGED FROM NASA CALC THE INDICATES (r) WITH STRING, INVESTIGATE REMOVAL) + return ans; + } else if (circumstances[40] == 3) { + //Set (CHANGED FROM NASA CALC THE INDICATES (s) WITH STRING, INVESTIGATE REMOVAL) + return ans; + } else { + return ans; + } + } + // Get the altitude + private static String GetAlt(Double[] circumstances) { + Double t; + if (circumstances[40] == 2) { + return "0(r)"; + } + if (circumstances[40] == 3) { + return "0(s)"; + } + if (circumstances[32] < 0.0 && circumstances[32] >= -0.00524) { + // Crude correction for refraction (and for consistency's sake) + t = 0.0; + } else { + t = circumstances[32] * 180.0 / Math.PI; + } + String ans; + if (t < 0.0) { + ans = "-"; + t = -t; + } else { + ans = ""; + } + t = Math.Floor(t + 0.5); + if (t < 10.0) { + ans += "0"; + } + ans += t; + if (circumstances[40] == 1) { + //WAS ITALIC + return ans; + } else { + return ans; + } + } + // Get the azimuth + private static String GetAzi(Double[] circumstances) { + String ans = ""; + Double t = circumstances[35] * 180.0 / Math.PI; + if (t < 0.0) { + t += 360.0; + } + if (t >= 360.0) { + t -= 360.0; + } + t = Math.Floor(t + 0.5); + if (t < 100.0) { + ans += "0"; + } + if (t < 10.0) { + ans += "0"; + } + ans += t; + if (circumstances[40] == 1) { + //WAS ITALIC + return ans; + } else { + return ans; + } + } + // Get the magnitude + private static String GetMagnitude(Double[] mid) { + Double a = Math.Floor(1000.0 * mid[37] + 0.5) / 1000.0; + String ans = a.ToString(); + if (mid[40] == 1) { + return ans; + } + if (mid[40] == 2) { + ans = a.ToString() + "(r)"; + } + if (mid[40] == 3) { + ans = a.ToString() + "(s)"; + } + return ans; + } + // Get the coverage + private static String GetCoverage(Double[] mid) { + Double a = 0, b, c; + String ans; + if (mid[37] <= 0.0) { + ans = "0.0"; + } else if (mid[37] >= 1.0) { + ans = "1.000"; + } else { + if (mid[39] == 2) { + c = mid[38] * mid[38]; + } else { + c = Math.Acos((mid[28] * mid[28] + mid[29] * mid[29] - 2.0 * mid[36] * mid[36]) / (mid[28] * mid[28] - mid[29] * mid[29])); + b = Math.Acos((mid[28] * mid[29] + mid[36] * mid[36]) / mid[36] / (mid[28] + mid[29])); + a = Math.PI - b - c; + c = (mid[38] * mid[38] * a + b - mid[38] * Math.Sin(c)) / Math.PI; + } + a = Math.Floor(1000.0 * c + 0.5) / 1000.0; + ans = a.ToString(); + } + if (mid[40] == 1) { + //WAS ITALIC + return ans; + } + if (mid[40] == 2) { + ans = a.ToString() + "(r)"; + } + if (mid[40] == 3) { + ans = a + "(s)"; + } + return ans; + } + // Get the duration in mm:ss.s format + // Adapted from code written by Stephen McCann - 27/04/2001 + private static String GetDuration(Double[] mid, Double[] c2, Double[] c3) { + Double tmp; + String ans; + + tmp = c3[40] == 4 ? mid[1] - c2[1] : c2[40] == 4 ? c3[1] - mid[1] : c3[1] - c2[1]; + if (tmp < 0.0) { + tmp += 24.0; + } else if (tmp >= 24.0) { + tmp -= 24.0; + } + tmp = tmp * 60.0 - 60.0 * Math.Floor(tmp) + 0.05 / 60.0; + ans = Math.Floor(tmp) + "m"; + tmp = tmp * 60.0 - 60.0 * Math.Floor(tmp); + if (tmp < 10.0) { + ans += "0"; + } + ans += Math.Floor(tmp) + "s"; + return ans; + } + } } diff --git a/CoordinateSharp/Celestial.SunCalculations.cs b/CoordinateSharp/Celestial.SunCalculations.cs index 376d019..bf0f07e 100644 --- a/CoordinateSharp/Celestial.SunCalculations.cs +++ b/CoordinateSharp/Celestial.SunCalculations.cs @@ -1,374 +1,318 @@ using System; using System.Collections.Generic; -namespace CoordinateSharp -{ - internal class SunCalc - { - public static void CalculateSunTime(double lat, double longi, DateTime date, Celestial c,double offset = 0) - { - if (date.Year == 0001) { return; } //Return if date vaue hasn't been established. - DateTime actualDate = new DateTime(date.Year,date.Month,date.Day,0, 0, 0, DateTimeKind.Utc); - ////Sun Time Calculations - - //Get Julian - double lw = rad * -longi; - double phi = rad * lat; +namespace CoordinateSharp { + internal class SunCalc { + public static void CalculateSunTime(Double lat, Double longi, DateTime date, Celestial c, Double _ = 0) { + if (date.Year == 0001) { return; } //Return if date vaue hasn't been established. + DateTime actualDate = new DateTime(date.Year, date.Month, date.Day, 0, 0, 0, DateTimeKind.Utc); - //Rise Set - DateTime?[] evDate = Get_Event_Time(lw, phi, -.8333, actualDate); - c.sunRise = evDate[0]; - c.sunSet = evDate[1]; - - c.sunCondition = CelestialStatus.RiseAndSet; - //Azimuth and Altitude - CalculateSunAngle(date, longi, lat, c); - // neither sunrise nor sunset - if ((!c.SunRise.HasValue) && (!c.SunSet.HasValue)) - { - if (c.SunAltitude < 0) - { - c.sunCondition = CelestialStatus.DownAllDay; - } - else - { - c.sunCondition = CelestialStatus.UpAllDay; - } - } - // sunrise or sunset - else - { - if (!c.SunRise.HasValue) - { - // No sunrise this date - c.sunCondition = CelestialStatus.NoRise; + ////Sun Time Calculations - } - else if (!c.SunSet.HasValue) - { - // No sunset this date - c.sunCondition = CelestialStatus.NoSet; - } - } - //Additional Times - c.additionalSolarTimes = new AdditionalSolarTimes(); - //Dusk and Dawn - //Civil - evDate = Get_Event_Time(lw, phi, -6, actualDate); - c.AdditionalSolarTimes.CivilDawn = evDate[0]; - c.AdditionalSolarTimes.CivilDusk = evDate[1]; + //Get Julian + Double lw = rad * -longi; + Double phi = rad * lat; + //Rise Set + DateTime?[] evDate = Get_Event_Time(lw, phi, -.8333, actualDate); + c.sunRise = evDate[0]; + c.sunSet = evDate[1]; - //Nautical - evDate = Get_Event_Time(lw, phi, -12, actualDate); - c.AdditionalSolarTimes.NauticalDawn = evDate[0]; - c.AdditionalSolarTimes.NauticalDusk = evDate[1]; + c.sunCondition = CelestialStatus.RiseAndSet; + //Azimuth and Altitude + CalculateSunAngle(date, longi, lat, c); + // neither sunrise nor sunset + if (!c.SunRise.HasValue && !c.SunSet.HasValue) { + c.sunCondition = c.SunAltitude < 0 ? CelestialStatus.DownAllDay : CelestialStatus.UpAllDay; + } + // sunrise or sunset + else { + if (!c.SunRise.HasValue) { + // No sunrise this date + c.sunCondition = CelestialStatus.NoRise; - //Astronomical - evDate = Get_Event_Time(lw, phi, -18, actualDate); - - c.AdditionalSolarTimes.AstronomicalDawn = evDate[0]; - c.AdditionalSolarTimes.AstronomicalDusk = evDate[1]; - - //BottomDisc - evDate = Get_Event_Time(lw, phi, -.2998, actualDate); - c.AdditionalSolarTimes.SunriseBottomDisc = evDate[0]; - c.AdditionalSolarTimes.SunsetBottomDisc = evDate[1]; - - CalculateSolarEclipse(date, lat, longi, c); - - } - /// - /// Gets time of event based on specified degree below horizon - /// - /// Observer Longitude in radians - /// Observer Latitude in radians - /// Angle in Degrees - /// Date of Event - /// DateTime?[]{rise, set} - private static DateTime?[] Get_Event_Time(double lw, double phi, double h,DateTime date) - { - //Create arrays. Index 0 = Day -1, 1 = Day, 2 = Day + 1; - //These will be used to find exact day event occurs for comparison - DateTime?[] sets = new DateTime?[] { null, null, null, null, null }; - DateTime?[] rises = new DateTime?[] { null, null, null,null, null }; - - //Iterate starting with day -1; - for (int x = 0; x < 5; x++) - { - double d = JulianConversions.GetJulian(date.AddDays(x-2)) - j2000 + .5; //LESS PRECISE JULIAN NEEDED - - double n = julianCycle(d, lw); - double ds = approxTransit(0, lw, n); - - double M = solarMeanAnomaly(ds); - double L = eclipticLongitude(M); - - double dec = declination(L, 0); - - double Jnoon = solarTransitJ(ds, M, L); - - double Jset; - double Jrise; - - - DateTime? solarNoon = JulianConversions.GetDate_FromJulian(Jnoon); - DateTime? nadir = JulianConversions.GetDate_FromJulian(Jnoon - 0.5); - - //Rise Set - Jset = GetTime(h * rad, lw, phi, dec, n, M, L); - Jrise = Jnoon - (Jset - Jnoon); - - DateTime? rise = JulianConversions.GetDate_FromJulian(Jrise); - DateTime? set = JulianConversions.GetDate_FromJulian(Jset); - - rises[x] = rise; - sets[x] = set; - } - - //Compare and send - DateTime? tRise = null; - for(int x=0;x<5;x++) - { - if(rises[x].HasValue) - { - if(rises[x].Value.Day == date.Day) - { - tRise = rises[x]; - break; - } - } - } - DateTime? tSet = null; - for (int x = 0; x < 5; x++) - { - if (sets[x].HasValue) - { - if (sets[x].Value.Day == date.Day) - { - tSet = sets[x]; - break; - } - } - } - return new DateTime?[] { tRise, tSet }; + } else if (!c.SunSet.HasValue) { + // No sunset this date + c.sunCondition = CelestialStatus.NoSet; } + } + //Additional Times + c.additionalSolarTimes = new AdditionalSolarTimes(); + //Dusk and Dawn + //Civil + evDate = Get_Event_Time(lw, phi, -6, actualDate); + c.AdditionalSolarTimes.CivilDawn = evDate[0]; + c.AdditionalSolarTimes.CivilDusk = evDate[1]; - public static void CalculateZodiacSign(DateTime date, Celestial c) - { - //Aquarius (January 20 to February 18) - //Pisces (February 19 to March 20) - //Aries (March 21-April 19) - //Taurus (April 20-May 20) - //Gemini (May 21-June 20) - //Cancer (June 21-July 22) - //Leo (July 23-August 22) - //Virgo (August 23-September 22) - //Libra (September 23-October 22) - //Scorpio (October 23-November 21) - //Sagittarius (November 22-December 21) - //Capricorn (December 22-January 19) - if (date >= new DateTime(date.Year, 1, 1) && date <= new DateTime(date.Year, 1, 19, 23, 59, 59)) - { - c.AstrologicalSigns.ZodiacSign = "Capricorn"; - return; - } - if (date >= new DateTime(date.Year, 1, 20) && date <= new DateTime(date.Year, 2, 18, 23, 59, 59)) - { - c.AstrologicalSigns.ZodiacSign = "Aquarius"; - return; - } - if (date >= new DateTime(date.Year, 2, 19) && date <= new DateTime(date.Year, 3, 20, 23, 59, 59)) - { - c.AstrologicalSigns.ZodiacSign = "Pisces"; - return; - } - if (date >= new DateTime(date.Year, 3, 21) && date <= new DateTime(date.Year, 4, 19, 23, 59, 59)) - { - c.AstrologicalSigns.ZodiacSign = "Aries"; - return; - } - if (date >= new DateTime(date.Year, 4, 20) && date <= new DateTime(date.Year, 5, 20, 23, 59, 59)) - { - c.AstrologicalSigns.ZodiacSign = "Taurus"; - return; - } - if (date >= new DateTime(date.Year, 5, 21) && date <= new DateTime(date.Year, 6, 20,23,59,59)) - { - c.AstrologicalSigns.ZodiacSign = "Gemini"; - return; - } - if (date >= new DateTime(date.Year, 6, 21) && date <= new DateTime(date.Year, 7, 22, 23, 59, 59)) - { - c.AstrologicalSigns.ZodiacSign = "Cancer"; - return; - } - if (date >= new DateTime(date.Year, 7, 23) && date <= new DateTime(date.Year, 8, 22, 23, 59, 59)) - { - c.AstrologicalSigns.ZodiacSign = "Leo"; - return; - } - if (date >= new DateTime(date.Year, 8, 23) && date <= new DateTime(date.Year, 9, 22, 23, 59, 59)) - { - c.AstrologicalSigns.ZodiacSign = "Virgo"; - return; - } - if (date >= new DateTime(date.Year, 9, 23) && date <= new DateTime(date.Year, 10, 22, 23, 59, 59)) - { - c.AstrologicalSigns.ZodiacSign = "Libra"; - return; - } - if (date >= new DateTime(date.Year, 9, 23) && date <= new DateTime(date.Year, 11, 21, 23, 59, 59)) - { - c.AstrologicalSigns.ZodiacSign = "Scorpio"; - return; - } - if (date >= new DateTime(date.Year, 11, 21) && date <= new DateTime(date.Year, 12, 21, 23, 59, 59)) - { - c.AstrologicalSigns.ZodiacSign = "Sagittarius"; - return; - } - if (date >= new DateTime(date.Year, 12, 22) && date <= new DateTime(date.Year, 12, 31, 23, 59, 59)) - { - c.AstrologicalSigns.ZodiacSign = "Capricorn"; - return; - } - } - public static void CalculateSolarEclipse(DateTime date, double lat, double longi, Celestial c) - { - //Convert to Radian - double latR = lat * Math.PI / 180; - double longR = longi * Math.PI / 180; - List> se = SolarEclipseCalc.CalculateSolarEclipse(date, latR, longR); - //RETURN FIRST AND LAST - if (se.Count == 0) { return; } - //FIND LAST AND NEXT ECLIPSE - int lastE = -1; - int nextE = -1; - int currentE = 0; - DateTime lastDate = new DateTime(); - DateTime nextDate = new DateTime(3300, 1, 1); - //Iterate to get last and next eclipse - foreach(List values in se) - { - DateTime ld = DateTime.ParseExact(values[0], "yyyy-MMM-dd", System.Globalization.CultureInfo.InvariantCulture); - - if (ld < date && ld>lastDate) { lastDate = ld;lastE = currentE; } - if(ld>= date && ld < nextDate) { nextDate = ld;nextE = currentE; } - currentE++; - } - //SET ECLIPSE DATA - if (lastE >= 0) - { - c.SolarEclipse.LastEclipse = new SolarEclipseDetails(se[lastE]); - } - if (nextE >= 0) - { - c.SolarEclipse.NextEclipse = new SolarEclipseDetails(se[nextE]); - } - } - #region Private Suntime Members - private static readonly double dayMS = 1000 * 60 * 60 * 24, j1970 = 2440588, j2000 = 2451545; - private static readonly double rad = Math.PI / 180; + //Nautical + evDate = Get_Event_Time(lw, phi, -12, actualDate); + c.AdditionalSolarTimes.NauticalDawn = evDate[0]; + c.AdditionalSolarTimes.NauticalDusk = evDate[1]; - private static double LocalSiderealTimeForTimeZone(double lon, double jd, double z) - { - double s = 24110.5 + 8640184.812999999 * jd / 36525 + 86636.6 * z + 86400 * lon; - s = s / 86400; - s = s - Math.Truncate(s); - double lst = s * 360 *rad; - - return lst; - } - private static double SideRealTime(double d, double lw) - { - double s = rad * (280.16 + 360.9856235 * d) - lw; - return s; - } - private static double solarTransitJ(double ds, double M, double L) - { - return j2000 + ds + 0.0053 * Math.Sin(M) - 0.0069 * Math.Sin(2 * L); - } - - //CH15 - //Formula 15.1 - //Returns Approximate Time - private static double hourAngle(double h, double phi, double d) - { - //NUMBER RETURNING > and < 1 NaN; - double d1 = Math.Sin(h) - Math.Sin(phi) * Math.Sin(d); - double d2 = Math.Cos(phi) * Math.Cos(d); - double d3 = (d1 / d2); - - return Math.Acos(d3); - } - private static double approxTransit(double Ht, double lw, double n) - { - return 0.0009 + (Ht + lw) / (2 * Math.PI) + n; - } - - private static double julianCycle(double d, double lw) { return Math.Round(d - 0.0009 - lw / (2 * Math.PI)); } + //Astronomical + evDate = Get_Event_Time(lw, phi, -18, actualDate); - //Returns Time of specified event based on suns angle - private static double GetTime(double h, double lw, double phi, double dec, double n,double M, double L) - { - double approxTime = hourAngle(h, phi, dec); //Ch15 Formula 15.1 + c.AdditionalSolarTimes.AstronomicalDawn = evDate[0]; + c.AdditionalSolarTimes.AstronomicalDusk = evDate[1]; - double a = approxTransit(approxTime, lw, n); - double st = solarTransitJ(a, M, L); - - return st; - } - private static double declination(double l, double b) - { - double e = (Math.PI/180) * 23.4392911; // obliquity of the Earth - - return Math.Asin(Math.Sin(b) * Math.Cos(e) + Math.Cos(b) * Math.Sin(e) * Math.Sin(l)); - } - - private static void CalculateSunAngle(DateTime date, double longi, double lat, Celestial c) - { - TimeSpan ts = date - new DateTime(1970, 1, 1,0,0,0, DateTimeKind.Utc); - double dms = (ts.TotalMilliseconds / dayMS -.5 + j1970)-j2000; - - double lw = rad * -longi; - double phi = rad * lat; - double e = rad * 23.4397; - - double[] sc = sunCoords(dms); - - double H = SideRealTime(dms, lw) - sc[1]; + //BottomDisc + evDate = Get_Event_Time(lw, phi, -.2998, actualDate); + c.AdditionalSolarTimes.SunriseBottomDisc = evDate[0]; + c.AdditionalSolarTimes.SunsetBottomDisc = evDate[1]; - c.sunAzimuth = Math.Atan2(Math.Sin(H), Math.Cos(H) * Math.Sin(phi) - Math.Tan(sc[0]) * Math.Cos(phi)) * 180 / Math.PI + 180; - c.sunAltitude = Math.Asin(Math.Sin(phi) * Math.Sin(sc[0]) + Math.Cos(phi) * Math.Cos(sc[0]) * Math.Cos(H)) * 180 / Math.PI; - } + CalculateSolarEclipse(date, lat, longi, c); - private static double solarMeanAnomaly(double d) - { - return rad * (357.5291 + 0.98560028 * d); - } - - private static double eclipticLongitude(double m) - { - double c = rad * (1.9148 * Math.Sin(m) + 0.02 * Math.Sin(2 * m) + 0.0003 * Math.Sin(3 * m)); // equation of center - double p = rad * 102.9372; // perihelion of the Earth - - return m + c + p + Math.PI; - } - private static double[] sunCoords(double d) - { - - double m = solarMeanAnomaly(d); - double l = eclipticLongitude(m); - double[] sc = new double[2]; - double b = 0; - double e = rad * 23.4397; // obliquity of the Earth - sc[0] = Math.Asin(Math.Sin(b) * Math.Cos(e) + Math.Cos(b) * Math.Sin(e) * Math.Sin(l)); //declination - sc[1] = Math.Atan2(Math.Sin(l) * Math.Cos(e) - Math.Tan(b) * Math.Sin(e), Math.Cos(l)); //rightAscension - return sc; - } - #endregion - } + /// + /// Gets time of event based on specified degree below horizon + /// + /// Observer Longitude in radians + /// Observer Latitude in radians + /// Angle in Degrees + /// Date of Event + /// DateTime?[]{rise, set} + private static DateTime?[] Get_Event_Time(Double lw, Double phi, Double h, DateTime date) { + //Create arrays. Index 0 = Day -1, 1 = Day, 2 = Day + 1; + //These will be used to find exact day event occurs for comparison + DateTime?[] sets = new DateTime?[] { null, null, null, null, null }; + DateTime?[] rises = new DateTime?[] { null, null, null, null, null }; + + //Iterate starting with day -1; + for (Int32 x = 0; x < 5; x++) { + Double d = JulianConversions.GetJulian(date.AddDays(x - 2)) - j2000 + .5; //LESS PRECISE JULIAN NEEDED + + Double n = JulianCycle(d, lw); + Double ds = ApproxTransit(0, lw, n); + + Double M = SolarMeanAnomaly(ds); + Double L = EclipticLongitude(M); + + Double dec = Declination(L, 0); + + Double Jnoon = SolarTransitJ(ds, M, L); + + Double Jset; + Double Jrise; + + + //DateTime? solarNoon = JulianConversions.GetDate_FromJulian(Jnoon); + //DateTime? nadir = JulianConversions.GetDate_FromJulian(Jnoon - 0.5); + + //Rise Set + Jset = GetTime(h * rad, lw, phi, dec, n, M, L); + Jrise = Jnoon - (Jset - Jnoon); + + DateTime? rise = JulianConversions.GetDate_FromJulian(Jrise); + DateTime? set = JulianConversions.GetDate_FromJulian(Jset); + + rises[x] = rise; + sets[x] = set; + } + + //Compare and send + DateTime? tRise = null; + for (Int32 x = 0; x < 5; x++) { + if (rises[x].HasValue) { + if (rises[x].Value.Day == date.Day) { + tRise = rises[x]; + break; + } + } + } + DateTime? tSet = null; + for (Int32 x = 0; x < 5; x++) { + if (sets[x].HasValue) { + if (sets[x].Value.Day == date.Day) { + tSet = sets[x]; + break; + } + } + } + return new DateTime?[] { tRise, tSet }; + } + + public static void CalculateZodiacSign(DateTime date, Celestial c) { + //Aquarius (January 20 to February 18) + //Pisces (February 19 to March 20) + //Aries (March 21-April 19) + //Taurus (April 20-May 20) + //Gemini (May 21-June 20) + //Cancer (June 21-July 22) + //Leo (July 23-August 22) + //Virgo (August 23-September 22) + //Libra (September 23-October 22) + //Scorpio (October 23-November 21) + //Sagittarius (November 22-December 21) + //Capricorn (December 22-January 19) + if (date >= new DateTime(date.Year, 1, 1) && date <= new DateTime(date.Year, 1, 19, 23, 59, 59)) { + c.AstrologicalSigns.ZodiacSign = "Capricorn"; + return; + } + if (date >= new DateTime(date.Year, 1, 20) && date <= new DateTime(date.Year, 2, 18, 23, 59, 59)) { + c.AstrologicalSigns.ZodiacSign = "Aquarius"; + return; + } + if (date >= new DateTime(date.Year, 2, 19) && date <= new DateTime(date.Year, 3, 20, 23, 59, 59)) { + c.AstrologicalSigns.ZodiacSign = "Pisces"; + return; + } + if (date >= new DateTime(date.Year, 3, 21) && date <= new DateTime(date.Year, 4, 19, 23, 59, 59)) { + c.AstrologicalSigns.ZodiacSign = "Aries"; + return; + } + if (date >= new DateTime(date.Year, 4, 20) && date <= new DateTime(date.Year, 5, 20, 23, 59, 59)) { + c.AstrologicalSigns.ZodiacSign = "Taurus"; + return; + } + if (date >= new DateTime(date.Year, 5, 21) && date <= new DateTime(date.Year, 6, 20, 23, 59, 59)) { + c.AstrologicalSigns.ZodiacSign = "Gemini"; + return; + } + if (date >= new DateTime(date.Year, 6, 21) && date <= new DateTime(date.Year, 7, 22, 23, 59, 59)) { + c.AstrologicalSigns.ZodiacSign = "Cancer"; + return; + } + if (date >= new DateTime(date.Year, 7, 23) && date <= new DateTime(date.Year, 8, 22, 23, 59, 59)) { + c.AstrologicalSigns.ZodiacSign = "Leo"; + return; + } + if (date >= new DateTime(date.Year, 8, 23) && date <= new DateTime(date.Year, 9, 22, 23, 59, 59)) { + c.AstrologicalSigns.ZodiacSign = "Virgo"; + return; + } + if (date >= new DateTime(date.Year, 9, 23) && date <= new DateTime(date.Year, 10, 22, 23, 59, 59)) { + c.AstrologicalSigns.ZodiacSign = "Libra"; + return; + } + if (date >= new DateTime(date.Year, 9, 23) && date <= new DateTime(date.Year, 11, 21, 23, 59, 59)) { + c.AstrologicalSigns.ZodiacSign = "Scorpio"; + return; + } + if (date >= new DateTime(date.Year, 11, 21) && date <= new DateTime(date.Year, 12, 21, 23, 59, 59)) { + c.AstrologicalSigns.ZodiacSign = "Sagittarius"; + return; + } + if (date >= new DateTime(date.Year, 12, 22) && date <= new DateTime(date.Year, 12, 31, 23, 59, 59)) { + c.AstrologicalSigns.ZodiacSign = "Capricorn"; + return; + } + } + public static void CalculateSolarEclipse(DateTime date, Double lat, Double longi, Celestial c) { + //Convert to Radian + Double latR = lat * Math.PI / 180; + Double longR = longi * Math.PI / 180; + List> se = SolarEclipseCalc.CalculateSolarEclipse(date, latR, longR); + //RETURN FIRST AND LAST + if (se.Count == 0) { return; } + //FIND LAST AND NEXT ECLIPSE + Int32 lastE = -1; + Int32 nextE = -1; + Int32 currentE = 0; + DateTime lastDate = new DateTime(); + DateTime nextDate = new DateTime(3300, 1, 1); + //Iterate to get last and next eclipse + foreach (List values in se) { + DateTime ld = DateTime.ParseExact(values[0], "yyyy-MMM-dd", System.Globalization.CultureInfo.InvariantCulture); + + if (ld < date && ld > lastDate) { lastDate = ld; lastE = currentE; } + if (ld >= date && ld < nextDate) { nextDate = ld; nextE = currentE; } + currentE++; + } + //SET ECLIPSE DATA + if (lastE >= 0) { + c.SolarEclipse.LastEclipse = new SolarEclipseDetails(se[lastE]); + } + if (nextE >= 0) { + c.SolarEclipse.NextEclipse = new SolarEclipseDetails(se[nextE]); + } + } + + #region Private Suntime Members + private static readonly Double dayMS = 1000 * 60 * 60 * 24, j1970 = 2440588, j2000 = 2451545; + private static readonly Double rad = Math.PI / 180; + + /*private static Double LocalSiderealTimeForTimeZone(Double lon, Double jd, Double z) + { + Double s = 24110.5 + 8640184.812999999 * jd / 36525 + 86636.6 * z + 86400 * lon; + s = s / 86400; + s = s - Math.Truncate(s); + Double lst = s * 360 *rad; + + return lst; + }*/ + private static Double SideRealTime(Double d, Double lw) { + Double s = rad * (280.16 + 360.9856235 * d) - lw; + return s; + } + private static Double SolarTransitJ(Double ds, Double M, Double L) => j2000 + ds + 0.0053 * Math.Sin(M) - 0.0069 * Math.Sin(2 * L); + + //CH15 + //Formula 15.1 + //Returns Approximate Time + private static Double HourAngle(Double h, Double phi, Double d) { + //NUMBER RETURNING > and < 1 NaN; + Double d1 = Math.Sin(h) - Math.Sin(phi) * Math.Sin(d); + Double d2 = Math.Cos(phi) * Math.Cos(d); + Double d3 = d1 / d2; + + return Math.Acos(d3); + } + private static Double ApproxTransit(Double Ht, Double lw, Double n) => 0.0009 + (Ht + lw) / (2 * Math.PI) + n; + + private static Double JulianCycle(Double d, Double lw) => Math.Round(d - 0.0009 - lw / (2 * Math.PI)); + + //Returns Time of specified event based on suns angle + private static Double GetTime(Double h, Double lw, Double phi, Double dec, Double n, Double M, Double L) { + Double approxTime = HourAngle(h, phi, dec); //Ch15 Formula 15.1 + + Double a = ApproxTransit(approxTime, lw, n); + Double st = SolarTransitJ(a, M, L); + + return st; + } + private static Double Declination(Double l, Double b) { + Double e = Math.PI / 180 * 23.4392911; // obliquity of the Earth + + return Math.Asin(Math.Sin(b) * Math.Cos(e) + Math.Cos(b) * Math.Sin(e) * Math.Sin(l)); + } + + private static void CalculateSunAngle(DateTime date, Double longi, Double lat, Celestial c) { + TimeSpan ts = date - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + Double dms = ts.TotalMilliseconds / dayMS - .5 + j1970 - j2000; + + Double lw = rad * -longi; + Double phi = rad * lat; + //Double e = rad * 23.4397; + + Double[] sc = SunCoords(dms); + + Double H = SideRealTime(dms, lw) - sc[1]; + + c.sunAzimuth = Math.Atan2(Math.Sin(H), Math.Cos(H) * Math.Sin(phi) - Math.Tan(sc[0]) * Math.Cos(phi)) * 180 / Math.PI + 180; + c.sunAltitude = Math.Asin(Math.Sin(phi) * Math.Sin(sc[0]) + Math.Cos(phi) * Math.Cos(sc[0]) * Math.Cos(H)) * 180 / Math.PI; + } + + private static Double SolarMeanAnomaly(Double d) => rad * (357.5291 + 0.98560028 * d); + + private static Double EclipticLongitude(Double m) { + Double c = rad * (1.9148 * Math.Sin(m) + 0.02 * Math.Sin(2 * m) + 0.0003 * Math.Sin(3 * m)); // equation of center + Double p = rad * 102.9372; // perihelion of the Earth + + return m + c + p + Math.PI; + } + private static Double[] SunCoords(Double d) { + + Double m = SolarMeanAnomaly(d); + Double l = EclipticLongitude(m); + Double[] sc = new Double[2]; + Double b = 0; + Double e = rad * 23.4397; // obliquity of the Earth + sc[0] = Math.Asin(Math.Sin(b) * Math.Cos(e) + Math.Cos(b) * Math.Sin(e) * Math.Sin(l)); //declination + sc[1] = Math.Atan2(Math.Sin(l) * Math.Cos(e) - Math.Tan(b) * Math.Sin(e), Math.Cos(l)); //rightAscension + return sc; + } + #endregion + + } } diff --git a/CoordinateSharp/Celestial.cs b/CoordinateSharp/Celestial.cs index a7f2d90..5bdf65b 100644 --- a/CoordinateSharp/Celestial.cs +++ b/CoordinateSharp/Celestial.cs @@ -1,620 +1,520 @@ using System; -using System.ComponentModel; using System.Collections.Generic; -namespace CoordinateSharp -{ + +namespace CoordinateSharp { + /// + /// The main class for handling location based celestial information. + /// + /// + /// This class can calculate various pieces of solar and lunar data, based on location and date + /// + [Serializable] + public class Celestial { + + //When a rise or a set does not occur, the DateTime will return null + /// - /// The main class for handling location based celestial information. + /// Creates an empty Celestial. /// - /// - /// This class can calculate various pieces of solar and lunar data, based on location and date - /// - [Serializable] - public class Celestial - { - - //When a rise or a set does not occur, the DateTime will return null - - /// - /// Creates an empty Celestial. - /// - public Celestial() - { - astrologicalSigns = new AstrologicalSigns(); - lunarEclipse = new LunarEclipse(); - solarEclipse = new SolarEclipse(); - CalculateCelestialTime(0, 0, new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc)); - } - - private Celestial(bool hasCalcs) - { - - astrologicalSigns = new AstrologicalSigns(); - lunarEclipse = new LunarEclipse(); - solarEclipse = new SolarEclipse(); - if (hasCalcs) { CalculateCelestialTime(0, 0, new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc)); } - } - - /// - /// Creates a Celestial based on a location and specified date - /// - /// latitude - /// longitude - /// DateTime (UTC) - public Celestial(double lat, double longi, DateTime geoDate) - { - DateTime d = new DateTime(geoDate.Year, geoDate.Month, geoDate.Day, geoDate.Hour, geoDate.Minute, geoDate.Second, DateTimeKind.Utc); - astrologicalSigns = new AstrologicalSigns(); - lunarEclipse = new LunarEclipse(); - solarEclipse = new SolarEclipse(); - CalculateCelestialTime(lat, longi, d); - } - - /// - /// Creates a Celestial based on a location and date in the provided Coordinate. - /// - /// Coordinate - /// Celestial - public static Celestial LoadCelestial(Coordinate c) - { - DateTime geoDate = c.GeoDate; - - DateTime d = new DateTime(geoDate.Year, geoDate.Month, geoDate.Day, geoDate.Hour, geoDate.Minute, geoDate.Second, DateTimeKind.Utc); - Celestial cel = new Celestial(c.Latitude.ToDouble(), c.Longitude.ToDouble(), c.GeoDate); - - return cel; - } - - /// - /// Converts Celestial values to local times. - /// - /// Coordinate - /// UTC offset - /// - public static Celestial Celestial_LocalTime(Coordinate c, double offset) - { - if(offset < -12 || offset > 12) { throw new ArgumentOutOfRangeException("Time offsets cannot be greater 12 or less than -12."); } - //Probably need to offset initial UTC date so user can op in local - //Determine best way to do this. - DateTime d = c.GeoDate.AddHours(offset); - - //Get 3 objects for comparison - Celestial cel = new Celestial(c.Latitude.ToDouble(), c.Longitude.ToDouble(), c.GeoDate); - Celestial celPre = new Celestial(c.Latitude.ToDouble(), c.Longitude.ToDouble(), c.GeoDate.AddDays(-1)); - Celestial celPost = new Celestial(c.Latitude.ToDouble(), c.Longitude.ToDouble(), c.GeoDate.AddDays(1)); - - //Slip objects for comparison. Compare with slipped date. - celPre.Local_Convert(c, offset); - cel.Local_Convert(c, offset); - celPost.Local_Convert(c, offset); - - //Get SunSet - int i = Determine_Slipped_Event_Index(cel.SunSet, celPre.SunSet, celPost.SunSet, d); - cel.sunSet = Get_Correct_Slipped_Date(cel.SunSet, celPre.SunSet, celPost.SunSet, i); - cel.AdditionalSolarTimes.CivilDusk = Get_Correct_Slipped_Date(cel.AdditionalSolarTimes.CivilDusk, - celPre.AdditionalSolarTimes.CivilDusk, celPost.AdditionalSolarTimes.CivilDusk, i); - cel.AdditionalSolarTimes.NauticalDusk = Get_Correct_Slipped_Date(cel.AdditionalSolarTimes.NauticalDusk, - celPre.AdditionalSolarTimes.NauticalDusk, celPost.AdditionalSolarTimes.NauticalDusk, i); - //Get SunRise - i = Determine_Slipped_Event_Index(cel.SunRise, celPre.SunRise, celPost.SunRise, d); - cel.sunRise = Get_Correct_Slipped_Date(cel.SunRise, celPre.SunRise, celPost.SunRise, i); - cel.AdditionalSolarTimes.CivilDawn = Get_Correct_Slipped_Date(cel.AdditionalSolarTimes.CivilDawn, - celPre.AdditionalSolarTimes.CivilDawn, celPost.AdditionalSolarTimes.CivilDawn, i); - cel.AdditionalSolarTimes.NauticalDawn = Get_Correct_Slipped_Date(cel.AdditionalSolarTimes.NauticalDawn, - celPre.AdditionalSolarTimes.NauticalDawn, celPost.AdditionalSolarTimes.NauticalDawn, i); - - //MoonRise - i = Determine_Slipped_Event_Index(cel.MoonRise, celPre.MoonRise, celPost.MoonRise, d); - cel.moonRise = Get_Correct_Slipped_Date(cel.MoonRise, celPre.MoonRise, celPost.MoonRise, i); - - //MoonSet - i = Determine_Slipped_Event_Index(cel.MoonSet, celPre.MoonSet, celPost.MoonSet, d); - cel.moonSet = Get_Correct_Slipped_Date(cel.MoonSet, celPre.MoonSet, celPost.MoonSet, i); - - //Local Conditions - CelestialStatus[] cels = new CelestialStatus[] - { - celPre.MoonCondition,cel.MoonCondition,celPost.MoonCondition - }; - cel.moonCondition = Celestial.GetStatus(cel.MoonRise, cel.MoonSet, cels); - cels = new CelestialStatus[] - { - celPre.SunCondition, cel.SunCondition, celPost.SunCondition - }; - cel.sunCondition = Celestial.GetStatus(cel.SunRise, cel.SunSet, cels); - - //Load IsUp values based on local time with populated Celestial - Celestial.Calculate_Celestial_IsUp_Booleans(d, cel); - - return cel; - } - - private static CelestialStatus GetStatus(DateTime? rise, DateTime? set, CelestialStatus[] cels) - { - if (set.HasValue && rise.HasValue) { return CelestialStatus.RiseAndSet; } - if (set.HasValue && !rise.HasValue) { return CelestialStatus.NoRise; } - if (!set.HasValue && rise.HasValue) { return CelestialStatus.NoSet; } - for (int x=0; x < 3;x++) - { - if(cels[x] == CelestialStatus.DownAllDay || cels[x] == CelestialStatus.UpAllDay) - { - return cels[x]; - } - } - return cels[1]; - } - - /// - /// In place time slip - /// - /// Coordinate - /// hour offset - private void Local_Convert(Coordinate c, double offset) - { - //Find new lunar set rise times - if (MoonSet.HasValue) { moonSet = moonSet.Value.AddHours(offset); } - if (MoonRise.HasValue) { moonRise = moonRise.Value.AddHours(offset); } - //Perigee - Perigee.ConvertTo_Local_Time(offset); - //Apogee - Apogee.ConvertTo_Local_Time(offset); - //Eclipse - LunarEclipse.ConvertTo_LocalTime(offset); - - ////Solar - if (sunSet.HasValue) { sunSet = sunSet.Value.AddHours(offset); } - if (SunRise.HasValue) { sunRise = SunRise.Value.AddHours(offset); } - AdditionalSolarTimes.Convert_To_Local_Time(offset); - - //Eclipse - SolarEclipse.ConvertTo_LocalTime(offset); - SunCalc.CalculateZodiacSign(c.GeoDate.AddHours(offset), this); - MoonCalc.GetMoonSign(c.GeoDate.AddHours(offset), this); - - } - - private PerigeeApogee Get_Correct_Slipped_Date(PerigeeApogee actual, PerigeeApogee pre, PerigeeApogee post, int i) - { - switch (i) - { - case 0: - return pre; - case 1: - return actual; - case 2: - return post; - default: - return actual; - } - } - private static DateTime? Get_Correct_Slipped_Date(DateTime? actual, DateTime? pre, DateTime? post, int i) - { - switch(i) - { - case 0: - return pre; - case 1: - return actual; - case 2: - return post; - default: - return null; - } - } - private static int Determine_Slipped_Event_Index(DateTime? actual, DateTime? pre, DateTime? post, DateTime d) - { - - if (actual.HasValue) - { - if (actual.Value.Day != d.Day) - { - if (pre.HasValue) - { - if (pre.Value.Day == d.Day) { return 0; } - } - if (post.HasValue) - { - if (post.Value.Day == d.Day) { return 2; } - } - return 3; - } - } - else - { - if (pre.HasValue) - { - if (pre.Value.Day == d.Day) { return 0; } - } - if (post.HasValue) - { - if (post.Value.Day == d.Day) { return 2; } - } - } - return 1; - } - - internal DateTime? sunSet; - internal DateTime? sunRise; - internal DateTime? moonSet; - internal DateTime? moonRise; - - internal double sunAltitude; - internal double sunAzimuth; - internal double moonAltitude; - internal double moonAzimuth; - - internal Distance moonDistance; - - internal CelestialStatus sunCondition; - internal CelestialStatus moonCondition; - - internal bool isSunUp; - internal bool isMoonUp; - - internal MoonIllum moonIllum; - internal Perigee perigee; - internal Apogee apogee; - internal AdditionalSolarTimes additionalSolarTimes; - internal AstrologicalSigns astrologicalSigns; - internal SolarEclipse solarEclipse; - internal LunarEclipse lunarEclipse; - - /// - /// Sunset time. - /// - public DateTime? SunSet { get { return sunSet; } } - /// - /// Sunrise time. - /// - public DateTime? SunRise { get { return sunRise; } } - /// - /// Moonset time. - /// - public DateTime? MoonSet { get { return moonSet; } } - /// - /// Moonrise time. - /// - public DateTime? MoonRise { get { return moonRise; } } - - /// - /// Sun altitude in degrees (E of N). - /// - public double SunAltitude { get { return sunAltitude; } } - /// - /// Sun azimuth in degrees (E of N). - /// - public double SunAzimuth { get { return sunAzimuth; } } - /// - /// Moon altitude in degrees (corrected for parallax and refraction). - /// - public double MoonAltitude { get { return moonAltitude; } } - /// - /// Moon azimuth in degrees (E of N). - /// - public double MoonAzimuth { get { return moonAzimuth; } } - - /// - /// Estimated moon distance from the earth. - /// - public Distance MoonDistance { get { return moonDistance; } } - /// - /// Sun's Condition based on the provided date. - /// - public CelestialStatus SunCondition { get { return sunCondition; } } - /// - /// Moon's condition based on the provided date. - /// - public CelestialStatus MoonCondition { get { return moonCondition; } } - - /// - /// Determine if the sun is currently up, based on sunset and sunrise time at the provided location and date. - /// - public bool IsSunUp{ get { return isSunUp; } } - - /// - /// Determine if the moon is currently up, based on moonset and moonrise time at the provided location and date. - /// - public bool IsMoonUp { get { return isMoonUp; } } - - /// - /// Moon ilumination details based on the provided date. - /// - /// - /// Contains phase, phase name, fraction and angle - /// - public MoonIllum MoonIllum { get { return moonIllum; } } - /// - /// Moons perigee details based on the provided date. - /// - public Perigee Perigee { get { return perigee; } } - /// - /// Moons apogee details based on the provided date. - /// - public Apogee Apogee { get { return apogee; } } - - /// - /// Additional solar event times based on the provided date and location. - /// - /// Contains civil and nautical dawn and dusk times. - public AdditionalSolarTimes AdditionalSolarTimes { get { return additionalSolarTimes; } } - /// - /// Astrological signs based on the provided date. - /// - /// - /// Contains zodiac, moon sign and moon name during full moon events - /// - public AstrologicalSigns AstrologicalSigns { get { return astrologicalSigns; } } - - /// - /// Returns a SolarEclipse. - /// - public SolarEclipse SolarEclipse { get { return solarEclipse; } } - /// - /// Returns a LunarEclipse. - /// - public LunarEclipse LunarEclipse { get { return lunarEclipse; } } - - /// - /// Calculates all celestial data. Coordinates will notify as changes occur - /// - /// Decimal format latitude - /// Decimal format longitude - /// Geographic DateTime - internal void CalculateCelestialTime(double lat, double longi, DateTime date) - { - date = new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second, DateTimeKind.Utc); - SunCalc.CalculateSunTime(lat, longi, date, this); - MoonCalc.GetMoonTimes(date, lat, longi, this); - MoonCalc.GetMoonDistance(date, this); - - SunCalc.CalculateZodiacSign(date, this); - MoonCalc.GetMoonSign(date, this); - - MoonCalc.GetMoonIllumination(date, this,lat,longi); - - - perigee = MoonCalc.GetPerigeeEvents(date); - apogee = MoonCalc.GetApogeeEvents(date); - - Calculate_Celestial_IsUp_Booleans(date, this); - - } - /// - /// Calculate celestial data based on lat/long and date. - /// - /// Decimal format latitude - /// Decimal format longitude - /// Geographic DateTime - /// Fully populated Celestial object - public static Celestial CalculateCelestialTimes(double lat, double longi, DateTime date) - { - date = new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second, DateTimeKind.Utc); - - Celestial c = new Celestial(false); - - SunCalc.CalculateSunTime(lat, longi, date, c); - MoonCalc.GetMoonTimes(date, lat, longi, c); - MoonCalc.GetMoonDistance(date, c); - SunCalc.CalculateZodiacSign(date, c); - MoonCalc.GetMoonSign(date, c); - MoonCalc.GetMoonIllumination(date, c,lat,longi); - - c.perigee = MoonCalc.GetPerigeeEvents(date); - c.apogee = MoonCalc.GetApogeeEvents(date); - - Calculate_Celestial_IsUp_Booleans(date, c); - - return c; - } - - /// - /// Calculate sun data based on lat/long and date. - /// - /// latitude - /// longitude - /// DateTime - /// Celestial (Partially Populated) - public static Celestial CalculateSunData(double lat, double longi, DateTime date) - { - date = new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second, DateTimeKind.Utc); - - Celestial c = new Celestial(false); - SunCalc.CalculateSunTime(lat, longi, date, c); - SunCalc.CalculateZodiacSign(date, c); - - return c; - } - /// - /// Calculate moon data based on lat/long and date. - /// - /// latitude - /// longitude - /// DateTime - /// Celestial (Partially Populated) - public static Celestial CalculateMoonData(double lat, double longi, DateTime date) - { - date = new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second, DateTimeKind.Utc); - - Celestial c = new Celestial(false); - - MoonCalc.GetMoonTimes(date, lat, longi, c); - MoonCalc.GetMoonDistance(date, c); - MoonCalc.GetMoonSign(date, c); - MoonCalc.GetMoonIllumination(date, c,lat,longi); - - c.perigee = MoonCalc.GetPerigeeEvents(date); - c.apogee = MoonCalc.GetApogeeEvents(date); - - return c; - } - - /// - /// Returns a List containing solar eclipse data for the century. - /// Century return is based on the date passed. - /// - /// latitude - /// longitude - /// DateTime - /// - public static List Get_Solar_Eclipse_Table(double lat, double longi, DateTime date) - { - //Convert to Radians - double latR = lat * Math.PI / 180; - double longR = longi * Math.PI / 180; - //Get solar data based on date - double[] events = Eclipse.SolarData.SolarDateData_100Year(date); - //Return list of solar data. - return SolarEclipseCalc.CalculateSolarEclipse(date, latR, longR, events); - } - /// - /// Returns a List containing solar eclipse data for the century. - /// Century return is based on the date passed. - /// - /// latitude - /// longitude - /// DateTime - /// - public static List Get_Lunar_Eclipse_Table(double lat, double longi, DateTime date) - { - //Convert to Radians - double latR = lat * Math.PI / 180; - double longR = longi * Math.PI / 180; - //Get solar data based on date - double[] events = Eclipse.LunarData.LunarDateData_100Year(date); - //Return list of solar data. - return LunarEclipseCalc.CalculateLunarEclipse(date, latR, longR, events); - } - - /// - /// Set bool SunIsUp and MoonIsUp values - /// - /// Coordinate GeoDate - /// Celestial Object - private static void Calculate_Celestial_IsUp_Booleans(DateTime date, Celestial cel) - { - //SUN - switch (cel.SunCondition) - { - case CelestialStatus.DownAllDay: - cel.isSunUp = false; - break; - case CelestialStatus.UpAllDay: - cel.isSunUp = true; - break; - case CelestialStatus.NoRise: - if(date cel.SunRise) - { - cel.isSunUp = true; - } - else { cel.isSunUp = false; } - break; - case CelestialStatus.RiseAndSet: - if (cel.SunRise < cel.SunSet) - { - if (date > cel.SunRise && date < cel.SunSet) - { - cel.isSunUp = true; - } - else - { - cel.isSunUp = false; - } - } - else - { - if (date > cel.SunRise || date < cel.SunSet) - { - cel.isSunUp = true; - } - else - { - cel.isSunUp = false; - } - } - break; - default: - //Should never be reached. If reached, previous calculations failed somewhere. - break; - } - - //MOON - switch (cel.MoonCondition) - { - case CelestialStatus.DownAllDay: - cel.isMoonUp = false; - break; - case CelestialStatus.UpAllDay: - cel.isMoonUp = true; - break; - case CelestialStatus.NoRise: - if (date < cel.MoonSet) - { - cel.isMoonUp = true; - } - else { cel.isMoonUp = false; } - break; - case CelestialStatus.NoSet: - if (date > cel.MoonRise) - { - cel.isMoonUp = true; - } - else { cel.isMoonUp = false; } - break; - case CelestialStatus.RiseAndSet: - if (cel.MoonRise < cel.MoonSet) - { - if (date > cel.MoonRise && date < cel.MoonSet) - { - cel.isMoonUp = true; - } - else - { - cel.isMoonUp = false; - } - } - else - { - if (date > cel.MoonRise || date < cel.MoonSet) - { - cel.isMoonUp = true; - } - else - { - cel.isMoonUp = false; - } - } - break; - default: - //Should never be reached. If reached, previous calculations failed somewhere. - break; - } - } - - /// - /// Returns Apogee object containing last and next apogee based on the specified date. - /// - /// DateTime - /// Apogee - public static Apogee GetApogees(DateTime d) - { - return MoonCalc.GetApogeeEvents(d); - } - /// - /// Returns Perigee object containing last and next perigee based on the specified date. - /// - /// DateTime - /// Perigee - public static Perigee GetPerigees(DateTime d) - { - return MoonCalc.GetPerigeeEvents(d); - } + public Celestial() { + this.astrologicalSigns = new AstrologicalSigns(); + this.lunarEclipse = new LunarEclipse(); + this.solarEclipse = new SolarEclipse(); + this.CalculateCelestialTime(0, 0, new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc)); } + private Celestial(Boolean hasCalcs) { + + this.astrologicalSigns = new AstrologicalSigns(); + this.lunarEclipse = new LunarEclipse(); + this.solarEclipse = new SolarEclipse(); + if (hasCalcs) { this.CalculateCelestialTime(0, 0, new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc)); } + } + + /// + /// Creates a Celestial based on a location and specified date + /// + /// latitude + /// longitude + /// DateTime (UTC) + public Celestial(Double lat, Double longi, DateTime geoDate) { + DateTime d = new DateTime(geoDate.Year, geoDate.Month, geoDate.Day, geoDate.Hour, geoDate.Minute, geoDate.Second, DateTimeKind.Utc); + this.astrologicalSigns = new AstrologicalSigns(); + this.lunarEclipse = new LunarEclipse(); + this.solarEclipse = new SolarEclipse(); + this.CalculateCelestialTime(lat, longi, d); + } + + /// + /// Creates a Celestial based on a location and date in the provided Coordinate. + /// + /// Coordinate + /// Celestial + public static Celestial LoadCelestial(Coordinate c) { + //DateTime geoDate = c.GeoDate; + + //DateTime d = new DateTime(geoDate.Year, geoDate.Month, geoDate.Day, geoDate.Hour, geoDate.Minute, geoDate.Second, DateTimeKind.Utc); + Celestial cel = new Celestial(c.Latitude.ToDouble(), c.Longitude.ToDouble(), c.GeoDate); + + return cel; + } + + /// + /// Converts Celestial values to local times. + /// + /// Coordinate + /// UTC offset + /// + public static Celestial Celestial_LocalTime(Coordinate c, Double offset) { + if (offset < -12 || offset > 12) { throw new ArgumentOutOfRangeException("Time offsets cannot be greater 12 or less than -12."); } + //Probably need to offset initial UTC date so user can op in local + //Determine best way to do this. + DateTime d = c.GeoDate.AddHours(offset); + + //Get 3 objects for comparison + Celestial cel = new Celestial(c.Latitude.ToDouble(), c.Longitude.ToDouble(), c.GeoDate); + Celestial celPre = new Celestial(c.Latitude.ToDouble(), c.Longitude.ToDouble(), c.GeoDate.AddDays(-1)); + Celestial celPost = new Celestial(c.Latitude.ToDouble(), c.Longitude.ToDouble(), c.GeoDate.AddDays(1)); + + //Slip objects for comparison. Compare with slipped date. + celPre.Local_Convert(c, offset); + cel.Local_Convert(c, offset); + celPost.Local_Convert(c, offset); + + //Get SunSet + Int32 i = Determine_Slipped_Event_Index(cel.SunSet, celPre.SunSet, celPost.SunSet, d); + cel.sunSet = Get_Correct_Slipped_Date(cel.SunSet, celPre.SunSet, celPost.SunSet, i); + cel.AdditionalSolarTimes.CivilDusk = Get_Correct_Slipped_Date(cel.AdditionalSolarTimes.CivilDusk, + celPre.AdditionalSolarTimes.CivilDusk, celPost.AdditionalSolarTimes.CivilDusk, i); + cel.AdditionalSolarTimes.NauticalDusk = Get_Correct_Slipped_Date(cel.AdditionalSolarTimes.NauticalDusk, + celPre.AdditionalSolarTimes.NauticalDusk, celPost.AdditionalSolarTimes.NauticalDusk, i); + //Get SunRise + i = Determine_Slipped_Event_Index(cel.SunRise, celPre.SunRise, celPost.SunRise, d); + cel.sunRise = Get_Correct_Slipped_Date(cel.SunRise, celPre.SunRise, celPost.SunRise, i); + cel.AdditionalSolarTimes.CivilDawn = Get_Correct_Slipped_Date(cel.AdditionalSolarTimes.CivilDawn, + celPre.AdditionalSolarTimes.CivilDawn, celPost.AdditionalSolarTimes.CivilDawn, i); + cel.AdditionalSolarTimes.NauticalDawn = Get_Correct_Slipped_Date(cel.AdditionalSolarTimes.NauticalDawn, + celPre.AdditionalSolarTimes.NauticalDawn, celPost.AdditionalSolarTimes.NauticalDawn, i); + + //MoonRise + i = Determine_Slipped_Event_Index(cel.MoonRise, celPre.MoonRise, celPost.MoonRise, d); + cel.moonRise = Get_Correct_Slipped_Date(cel.MoonRise, celPre.MoonRise, celPost.MoonRise, i); + + //MoonSet + i = Determine_Slipped_Event_Index(cel.MoonSet, celPre.MoonSet, celPost.MoonSet, d); + cel.moonSet = Get_Correct_Slipped_Date(cel.MoonSet, celPre.MoonSet, celPost.MoonSet, i); + + //Local Conditions + CelestialStatus[] cels = new CelestialStatus[] + { + celPre.MoonCondition,cel.MoonCondition,celPost.MoonCondition + }; + cel.moonCondition = Celestial.GetStatus(cel.MoonRise, cel.MoonSet, cels); + cels = new CelestialStatus[] + { + celPre.SunCondition, cel.SunCondition, celPost.SunCondition + }; + cel.sunCondition = Celestial.GetStatus(cel.SunRise, cel.SunSet, cels); + + //Load IsUp values based on local time with populated Celestial + Celestial.Calculate_Celestial_IsUp_Booleans(d, cel); + + return cel; + } + + private static CelestialStatus GetStatus(DateTime? rise, DateTime? set, CelestialStatus[] cels) { + if (set.HasValue && rise.HasValue) { return CelestialStatus.RiseAndSet; } + if (set.HasValue && !rise.HasValue) { return CelestialStatus.NoRise; } + if (!set.HasValue && rise.HasValue) { return CelestialStatus.NoSet; } + for (Int32 x = 0; x < 3; x++) { + if (cels[x] == CelestialStatus.DownAllDay || cels[x] == CelestialStatus.UpAllDay) { + return cels[x]; + } + } + return cels[1]; + } + + /// + /// In place time slip + /// + /// Coordinate + /// hour offset + private void Local_Convert(Coordinate c, Double offset) { + //Find new lunar set rise times + if (this.MoonSet.HasValue) { this.moonSet = this.moonSet.Value.AddHours(offset); } + if (this.MoonRise.HasValue) { this.moonRise = this.moonRise.Value.AddHours(offset); } + //Perigee + this.Perigee.ConvertTo_Local_Time(offset); + //Apogee + this.Apogee.ConvertTo_Local_Time(offset); + //Eclipse + this.LunarEclipse.ConvertTo_LocalTime(offset); + + ////Solar + if (this.sunSet.HasValue) { this.sunSet = this.sunSet.Value.AddHours(offset); } + if (this.SunRise.HasValue) { this.sunRise = this.SunRise.Value.AddHours(offset); } + this.AdditionalSolarTimes.Convert_To_Local_Time(offset); + + //Eclipse + this.SolarEclipse.ConvertTo_LocalTime(offset); + SunCalc.CalculateZodiacSign(c.GeoDate.AddHours(offset), this); + MoonCalc.GetMoonSign(c.GeoDate.AddHours(offset), this); + + } + + /*private PerigeeApogee Get_Correct_Slipped_Date(PerigeeApogee actual, PerigeeApogee pre, PerigeeApogee post, Int32 i) + { + switch (i) + { + case 0: + return pre; + case 1: + return actual; + case 2: + return post; + default: + return actual; + } + }*/ + private static DateTime? Get_Correct_Slipped_Date(DateTime? actual, DateTime? pre, DateTime? post, Int32 i) => i switch + { + 0 => pre, + 1 => actual, + 2 => post, + _ => null, + }; + private static Int32 Determine_Slipped_Event_Index(DateTime? actual, DateTime? pre, DateTime? post, DateTime d) { + + if (actual.HasValue) { + if (actual.Value.Day != d.Day) { + if (pre.HasValue) { + if (pre.Value.Day == d.Day) { return 0; } + } + if (post.HasValue) { + if (post.Value.Day == d.Day) { return 2; } + } + return 3; + } + } else { + if (pre.HasValue) { + if (pre.Value.Day == d.Day) { return 0; } + } + if (post.HasValue) { + if (post.Value.Day == d.Day) { return 2; } + } + } + return 1; + } + + internal DateTime? sunSet; + internal DateTime? sunRise; + internal DateTime? moonSet; + internal DateTime? moonRise; + + internal Double sunAltitude; + internal Double sunAzimuth; + internal Double moonAltitude; + internal Double moonAzimuth; + + internal Distance moonDistance; + + internal CelestialStatus sunCondition; + internal CelestialStatus moonCondition; + + internal Boolean isSunUp; + internal Boolean isMoonUp; + + internal MoonIllum moonIllum; + internal Perigee perigee; + internal Apogee apogee; + internal AdditionalSolarTimes additionalSolarTimes; + internal AstrologicalSigns astrologicalSigns; + internal SolarEclipse solarEclipse; + internal LunarEclipse lunarEclipse; + + /// + /// Sunset time. + /// + public DateTime? SunSet => this.sunSet; + /// + /// Sunrise time. + /// + public DateTime? SunRise => this.sunRise; + /// + /// Moonset time. + /// + public DateTime? MoonSet => this.moonSet; + /// + /// Moonrise time. + /// + public DateTime? MoonRise => this.moonRise; + + /// + /// Sun altitude in degrees (E of N). + /// + public Double SunAltitude => this.sunAltitude; + /// + /// Sun azimuth in degrees (E of N). + /// + public Double SunAzimuth => this.sunAzimuth; + /// + /// Moon altitude in degrees (corrected for parallax and refraction). + /// + public Double MoonAltitude => this.moonAltitude; + /// + /// Moon azimuth in degrees (E of N). + /// + public Double MoonAzimuth => this.moonAzimuth; + + /// + /// Estimated moon distance from the earth. + /// + public Distance MoonDistance => this.moonDistance; + /// + /// Sun's Condition based on the provided date. + /// + public CelestialStatus SunCondition => this.sunCondition; + /// + /// Moon's condition based on the provided date. + /// + public CelestialStatus MoonCondition => this.moonCondition; + + /// + /// Determine if the sun is currently up, based on sunset and sunrise time at the provided location and date. + /// + public Boolean IsSunUp => this.isSunUp; + + /// + /// Determine if the moon is currently up, based on moonset and moonrise time at the provided location and date. + /// + public Boolean IsMoonUp => this.isMoonUp; + + /// + /// Moon ilumination details based on the provided date. + /// + /// + /// Contains phase, phase name, fraction and angle + /// + public MoonIllum MoonIllum => this.moonIllum; + /// + /// Moons perigee details based on the provided date. + /// + public Perigee Perigee => this.perigee; + /// + /// Moons apogee details based on the provided date. + /// + public Apogee Apogee => this.apogee; + + /// + /// Additional solar event times based on the provided date and location. + /// + /// Contains civil and nautical dawn and dusk times. + public AdditionalSolarTimes AdditionalSolarTimes => this.additionalSolarTimes; + /// + /// Astrological signs based on the provided date. + /// + /// + /// Contains zodiac, moon sign and moon name during full moon events + /// + public AstrologicalSigns AstrologicalSigns => this.astrologicalSigns; + + /// + /// Returns a SolarEclipse. + /// + public SolarEclipse SolarEclipse => this.solarEclipse; + /// + /// Returns a LunarEclipse. + /// + public LunarEclipse LunarEclipse => this.lunarEclipse; + + /// + /// Calculates all celestial data. Coordinates will notify as changes occur + /// + /// Decimal format latitude + /// Decimal format longitude + /// Geographic DateTime + internal void CalculateCelestialTime(Double lat, Double longi, DateTime date) { + date = new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second, DateTimeKind.Utc); + SunCalc.CalculateSunTime(lat, longi, date, this); + MoonCalc.GetMoonTimes(date, lat, longi, this); + MoonCalc.GetMoonDistance(date, this); + + SunCalc.CalculateZodiacSign(date, this); + MoonCalc.GetMoonSign(date, this); + + MoonCalc.GetMoonIllumination(date, this, lat, longi); + + + this.perigee = MoonCalc.GetPerigeeEvents(date); + this.apogee = MoonCalc.GetApogeeEvents(date); + + Calculate_Celestial_IsUp_Booleans(date, this); + + } + /// + /// Calculate celestial data based on lat/long and date. + /// + /// Decimal format latitude + /// Decimal format longitude + /// Geographic DateTime + /// Fully populated Celestial object + public static Celestial CalculateCelestialTimes(Double lat, Double longi, DateTime date) { + date = new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second, DateTimeKind.Utc); + + Celestial c = new Celestial(false); + + SunCalc.CalculateSunTime(lat, longi, date, c); + MoonCalc.GetMoonTimes(date, lat, longi, c); + MoonCalc.GetMoonDistance(date, c); + SunCalc.CalculateZodiacSign(date, c); + MoonCalc.GetMoonSign(date, c); + MoonCalc.GetMoonIllumination(date, c, lat, longi); + + c.perigee = MoonCalc.GetPerigeeEvents(date); + c.apogee = MoonCalc.GetApogeeEvents(date); + + Calculate_Celestial_IsUp_Booleans(date, c); + + return c; + } + + /// + /// Calculate sun data based on lat/long and date. + /// + /// latitude + /// longitude + /// DateTime + /// Celestial (Partially Populated) + public static Celestial CalculateSunData(Double lat, Double longi, DateTime date) { + date = new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second, DateTimeKind.Utc); + + Celestial c = new Celestial(false); + SunCalc.CalculateSunTime(lat, longi, date, c); + SunCalc.CalculateZodiacSign(date, c); + + return c; + } + /// + /// Calculate moon data based on lat/long and date. + /// + /// latitude + /// longitude + /// DateTime + /// Celestial (Partially Populated) + public static Celestial CalculateMoonData(Double lat, Double longi, DateTime date) { + date = new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second, DateTimeKind.Utc); + + Celestial c = new Celestial(false); + + MoonCalc.GetMoonTimes(date, lat, longi, c); + MoonCalc.GetMoonDistance(date, c); + MoonCalc.GetMoonSign(date, c); + MoonCalc.GetMoonIllumination(date, c, lat, longi); + + c.perigee = MoonCalc.GetPerigeeEvents(date); + c.apogee = MoonCalc.GetApogeeEvents(date); + + return c; + } + + /// + /// Returns a List containing solar eclipse data for the century. + /// Century return is based on the date passed. + /// + /// latitude + /// longitude + /// DateTime + /// + public static List Get_Solar_Eclipse_Table(Double lat, Double longi, DateTime date) { + //Convert to Radians + Double latR = lat * Math.PI / 180; + Double longR = longi * Math.PI / 180; + //Get solar data based on date + Double[] events = Eclipse.SolarData.SolarDateData_100Year(date); + //Return list of solar data. + return SolarEclipseCalc.CalculateSolarEclipse(date, latR, longR, events); + } + /// + /// Returns a List containing solar eclipse data for the century. + /// Century return is based on the date passed. + /// + /// latitude + /// longitude + /// DateTime + /// + public static List Get_Lunar_Eclipse_Table(Double lat, Double longi, DateTime date) { + //Convert to Radians + Double latR = lat * Math.PI / 180; + Double longR = longi * Math.PI / 180; + //Get solar data based on date + Double[] events = Eclipse.LunarData.LunarDateData_100Year(date); + //Return list of solar data. + return LunarEclipseCalc.CalculateLunarEclipse(date, latR, longR, events); + } + + /// + /// Set bool SunIsUp and MoonIsUp values + /// + /// Coordinate GeoDate + /// Celestial Object + private static void Calculate_Celestial_IsUp_Booleans(DateTime date, Celestial cel) { + //SUN + switch (cel.SunCondition) { + case CelestialStatus.DownAllDay: + cel.isSunUp = false; + break; + case CelestialStatus.UpAllDay: + cel.isSunUp = true; + break; + case CelestialStatus.NoRise: + cel.isSunUp = date < cel.SunSet; + break; + case CelestialStatus.NoSet: + cel.isSunUp = date > cel.SunRise; + break; + case CelestialStatus.RiseAndSet: + cel.isSunUp = cel.SunRise < cel.SunSet ? date > cel.SunRise && date < cel.SunSet : date > cel.SunRise || date < cel.SunSet; + break; + default: + //Should never be reached. If reached, previous calculations failed somewhere. + break; + } + + //MOON + switch (cel.MoonCondition) { + case CelestialStatus.DownAllDay: + cel.isMoonUp = false; + break; + case CelestialStatus.UpAllDay: + cel.isMoonUp = true; + break; + case CelestialStatus.NoRise: + cel.isMoonUp = date < cel.MoonSet; + break; + case CelestialStatus.NoSet: + cel.isMoonUp = date > cel.MoonRise; + break; + case CelestialStatus.RiseAndSet: + cel.isMoonUp = cel.MoonRise < cel.MoonSet ? date > cel.MoonRise && date < cel.MoonSet : date > cel.MoonRise || date < cel.MoonSet; + break; + default: + //Should never be reached. If reached, previous calculations failed somewhere. + break; + } + } + + /// + /// Returns Apogee object containing last and next apogee based on the specified date. + /// + /// DateTime + /// Apogee + public static Apogee GetApogees(DateTime d) => MoonCalc.GetApogeeEvents(d); + /// + /// Returns Perigee object containing last and next perigee based on the specified date. + /// + /// DateTime + /// Perigee + public static Perigee GetPerigees(DateTime d) => MoonCalc.GetPerigeeEvents(d); + } + } diff --git a/CoordinateSharp/Coordinate.Assistant.cs b/CoordinateSharp/Coordinate.Assistant.cs index d4f06ff..5bf1857 100644 --- a/CoordinateSharp/Coordinate.Assistant.cs +++ b/CoordinateSharp/Coordinate.Assistant.cs @@ -2,268 +2,233 @@ using System.Collections.Generic; using System.Linq; -namespace CoordinateSharp -{ - +namespace CoordinateSharp { - - /// - /// Used for UTM/MGRS Conversions - /// - [Serializable] - internal class LatZones - { - public static List longZongLetters = new List(new string[]{"C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", + + + /// + /// Used for UTM/MGRS Conversions + /// + [Serializable] + internal class LatZones { + public static List longZongLetters = new List(new String[]{"C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "U", "V", "W", "X"}); + } + /// + /// Used for handling diagraph determination + /// + [Serializable] + internal class Digraphs { + private readonly List digraph1; + private readonly List digraph2; + + private readonly String[] digraph1Array = { "A", "B", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" }; + + private readonly String[] digraph2Array = { "V", "A", "B", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "U", "V" }; + + public Digraphs() { + this.digraph1 = new List(); + this.digraph2 = new List(); + + this.digraph1.Add(new Digraph() { Zone = 1, Letter = "A" }); + this.digraph1.Add(new Digraph() { Zone = 2, Letter = "B" }); + this.digraph1.Add(new Digraph() { Zone = 3, Letter = "C" }); + this.digraph1.Add(new Digraph() { Zone = 4, Letter = "D" }); + this.digraph1.Add(new Digraph() { Zone = 5, Letter = "E" }); + this.digraph1.Add(new Digraph() { Zone = 6, Letter = "F" }); + this.digraph1.Add(new Digraph() { Zone = 7, Letter = "G" }); + this.digraph1.Add(new Digraph() { Zone = 8, Letter = "H" }); + this.digraph1.Add(new Digraph() { Zone = 9, Letter = "J" }); + this.digraph1.Add(new Digraph() { Zone = 10, Letter = "K" }); + this.digraph1.Add(new Digraph() { Zone = 11, Letter = "L" }); + this.digraph1.Add(new Digraph() { Zone = 12, Letter = "M" }); + this.digraph1.Add(new Digraph() { Zone = 13, Letter = "N" }); + this.digraph1.Add(new Digraph() { Zone = 14, Letter = "P" }); + this.digraph1.Add(new Digraph() { Zone = 15, Letter = "Q" }); + this.digraph1.Add(new Digraph() { Zone = 16, Letter = "R" }); + this.digraph1.Add(new Digraph() { Zone = 17, Letter = "S" }); + this.digraph1.Add(new Digraph() { Zone = 18, Letter = "T" }); + this.digraph1.Add(new Digraph() { Zone = 19, Letter = "U" }); + this.digraph1.Add(new Digraph() { Zone = 20, Letter = "V" }); + this.digraph1.Add(new Digraph() { Zone = 21, Letter = "W" }); + this.digraph1.Add(new Digraph() { Zone = 22, Letter = "X" }); + this.digraph1.Add(new Digraph() { Zone = 23, Letter = "Y" }); + this.digraph1.Add(new Digraph() { Zone = 24, Letter = "Z" }); + this.digraph1.Add(new Digraph() { Zone = 1, Letter = "A" }); + + this.digraph2.Add(new Digraph() { Zone = 0, Letter = "V" }); + this.digraph2.Add(new Digraph() { Zone = 1, Letter = "A" }); + this.digraph2.Add(new Digraph() { Zone = 2, Letter = "B" }); + this.digraph2.Add(new Digraph() { Zone = 3, Letter = "C" }); + this.digraph2.Add(new Digraph() { Zone = 4, Letter = "D" }); + this.digraph2.Add(new Digraph() { Zone = 5, Letter = "E" }); + this.digraph2.Add(new Digraph() { Zone = 6, Letter = "F" }); + this.digraph2.Add(new Digraph() { Zone = 7, Letter = "G" }); + this.digraph2.Add(new Digraph() { Zone = 8, Letter = "H" }); + this.digraph2.Add(new Digraph() { Zone = 9, Letter = "J" }); + this.digraph2.Add(new Digraph() { Zone = 10, Letter = "K" }); + this.digraph2.Add(new Digraph() { Zone = 11, Letter = "L" }); + this.digraph2.Add(new Digraph() { Zone = 12, Letter = "M" }); + this.digraph2.Add(new Digraph() { Zone = 13, Letter = "N" }); + this.digraph2.Add(new Digraph() { Zone = 14, Letter = "P" }); + this.digraph2.Add(new Digraph() { Zone = 15, Letter = "Q" }); + this.digraph2.Add(new Digraph() { Zone = 16, Letter = "R" }); + this.digraph2.Add(new Digraph() { Zone = 17, Letter = "S" }); + this.digraph2.Add(new Digraph() { Zone = 18, Letter = "T" }); + this.digraph2.Add(new Digraph() { Zone = 19, Letter = "U" }); + this.digraph2.Add(new Digraph() { Zone = 20, Letter = "V" }); } + + internal Int32 GetDigraph1Index(String letter) { + for (Int32 i = 0; i < this.digraph1Array.Length; i++) { + if (this.digraph1Array[i].Equals(letter)) { + return i + 1; + } + } + + return -1; + } + + internal Int32 GetDigraph2Index(String letter) { + for (Int32 i = 0; i < this.digraph2Array.Length; i++) { + if (this.digraph2Array[i].Equals(letter)) { + return i; + } + } + + return -1; + } + + internal String GetDigraph1(Int32 longZone, Double easting) { + Int32 a1 = longZone; + Double a2 = 8 * ((a1 - 1) % 3) + 1; + + Double a3 = easting; + Double a4 = a2 + (Int32)(a3 / 100000) - 1; + return this.digraph1.Where(x => x.Zone == Math.Floor(a4)).FirstOrDefault().Letter; + } + + internal String GetDigraph2(Int32 longZone, Double northing) { + Int32 a1 = longZone; + Double a2 = 1 + 5 * ((a1 - 1) % 2); + Double a3 = northing; + Double a4 = a2 + (Int32)(a3 / 100000); + a4 = (a2 + (Int32)(a3 / 100000.0)) % 20; + a4 = Math.Floor(a4); + if (a4 < 0) { + a4 += 19; + } + return this.digraph2.Where(x => x.Zone == Math.Floor(a4)).FirstOrDefault().Letter; + } + + } + /// + /// Diagraph model + /// + [Serializable] + internal class Digraph { + public Int32 Zone { get; set; } + public String Letter { get; set; } + } + /// + /// Used for setting whether a coordinate part is latitudinal or longitudinal. + /// + [Serializable] + public enum CoordinateType { /// - /// Used for handling diagraph determination + /// Latitude /// - [Serializable] - internal class Digraphs - { - private List digraph1; - private List digraph2; - - private String[] digraph1Array = { "A", "B", "C", "D", "E", "F", "G", "H", - "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "U", "V", "W", "X", - "Y", "Z" }; - - private String[] digraph2Array = { "V", "A", "B", "C", "D", "E", "F", "G", - "H", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "U", "V" }; - - public Digraphs() - { - digraph1 = new List(); - digraph2 = new List(); - - digraph1.Add(new Digraph() { Zone = 1, Letter = "A" }); - digraph1.Add(new Digraph() { Zone = 2, Letter = "B" }); - digraph1.Add(new Digraph() { Zone = 3, Letter = "C" }); - digraph1.Add(new Digraph() { Zone = 4, Letter = "D" }); - digraph1.Add(new Digraph() { Zone = 5, Letter = "E" }); - digraph1.Add(new Digraph() { Zone = 6, Letter = "F" }); - digraph1.Add(new Digraph() { Zone = 7, Letter = "G" }); - digraph1.Add(new Digraph() { Zone = 8, Letter = "H" }); - digraph1.Add(new Digraph() { Zone = 9, Letter = "J" }); - digraph1.Add(new Digraph() { Zone = 10, Letter = "K" }); - digraph1.Add(new Digraph() { Zone = 11, Letter = "L" }); - digraph1.Add(new Digraph() { Zone = 12, Letter = "M" }); - digraph1.Add(new Digraph() { Zone = 13, Letter = "N" }); - digraph1.Add(new Digraph() { Zone = 14, Letter = "P" }); - digraph1.Add(new Digraph() { Zone = 15, Letter = "Q" }); - digraph1.Add(new Digraph() { Zone = 16, Letter = "R" }); - digraph1.Add(new Digraph() { Zone = 17, Letter = "S" }); - digraph1.Add(new Digraph() { Zone = 18, Letter = "T" }); - digraph1.Add(new Digraph() { Zone = 19, Letter = "U" }); - digraph1.Add(new Digraph() { Zone = 20, Letter = "V" }); - digraph1.Add(new Digraph() { Zone = 21, Letter = "W" }); - digraph1.Add(new Digraph() { Zone = 22, Letter = "X" }); - digraph1.Add(new Digraph() { Zone = 23, Letter = "Y" }); - digraph1.Add(new Digraph() { Zone = 24, Letter = "Z" }); - digraph1.Add(new Digraph() { Zone = 1, Letter = "A" }); - - digraph2.Add(new Digraph() { Zone = 0, Letter = "V"}); - digraph2.Add(new Digraph() { Zone = 1, Letter = "A" }); - digraph2.Add(new Digraph() { Zone = 2, Letter = "B" }); - digraph2.Add(new Digraph() { Zone = 3, Letter = "C" }); - digraph2.Add(new Digraph() { Zone = 4, Letter = "D" }); - digraph2.Add(new Digraph() { Zone = 5, Letter = "E" }); - digraph2.Add(new Digraph() { Zone = 6, Letter = "F" }); - digraph2.Add(new Digraph() { Zone = 7, Letter = "G" }); - digraph2.Add(new Digraph() { Zone = 8, Letter = "H" }); - digraph2.Add(new Digraph() { Zone = 9, Letter = "J" }); - digraph2.Add(new Digraph() { Zone = 10, Letter = "K" }); - digraph2.Add(new Digraph() { Zone = 11, Letter = "L" }); - digraph2.Add(new Digraph() { Zone = 12, Letter = "M" }); - digraph2.Add(new Digraph() { Zone = 13, Letter = "N" }); - digraph2.Add(new Digraph() { Zone = 14, Letter = "P" }); - digraph2.Add(new Digraph() { Zone = 15, Letter = "Q" }); - digraph2.Add(new Digraph() { Zone = 16, Letter = "R" }); - digraph2.Add(new Digraph() { Zone = 17, Letter = "S" }); - digraph2.Add(new Digraph() { Zone = 18, Letter = "T" }); - digraph2.Add(new Digraph() { Zone = 19, Letter = "U" }); - digraph2.Add(new Digraph() { Zone = 20, Letter = "V" }); - } - - internal int getDigraph1Index(String letter) - { - for (int i = 0; i < digraph1Array.Length; i++) - { - if (digraph1Array[i].Equals(letter)) - { - return i + 1; - } - } - - return -1; - } - - internal int getDigraph2Index(String letter) - { - for (int i = 0; i < digraph2Array.Length; i++) - { - if (digraph2Array[i].Equals(letter)) - { - return i; - } - } - - return -1; - } - - internal String getDigraph1(int longZone, double easting) - { - int a1 = longZone; - double a2 = 8 * ((a1 - 1) % 3) + 1; - - double a3 = easting; - double a4 = a2 + ((int)(a3 / 100000)) - 1; - return digraph1.Where(x=>x.Zone == Math.Floor(a4)).FirstOrDefault().Letter; - } - - internal String getDigraph2(int longZone, double northing) - { - int a1 = longZone; - double a2 = 1 + 5 * ((a1 - 1) % 2); - double a3 = northing; - double a4 = (a2 + ((int)(a3 / 100000))); - a4 = (a2 + ((int)(a3 / 100000.0))) % 20; - a4 = Math.Floor(a4); - if (a4 < 0) - { - a4 = a4 + 19; - } - return digraph2.Where(x => x.Zone == Math.Floor(a4)).FirstOrDefault().Letter; - } - - } + Lat, /// - /// Diagraph model + /// Longitude /// - [Serializable] - internal class Digraph - { - public int Zone { get; set; } - public string Letter { get; set; } - } + Long + } + /// + /// Used to set a coordinate part position. + /// + [Serializable] + public enum CoordinatesPosition : Int32 { /// - /// Used for setting whether a coordinate part is latitudinal or longitudinal. + /// North /// - [Serializable] - public enum CoordinateType - { - /// - /// Latitude - /// - Lat, - /// - /// Longitude - /// - Long - } + N, /// - /// Used to set a coordinate part position. + /// East /// - [Serializable] - public enum CoordinatesPosition :int - { - /// - /// North - /// - N, - /// - /// East - /// - E, - /// - /// South - /// - S, - /// - /// West - /// - W - } - - + E, /// - /// Coordinate type datum specification + /// South /// - [Serializable] - [Flags] - public enum Coordinate_Datum - { - /// - /// Lat Long GeoDetic - /// - LAT_LONG = 1, - /// - /// UTM and MGRS - /// - UTM_MGRS = 2, - /// - /// ECEF - /// - ECEF = 4, - } - + S, /// - /// Cartesian Coordinate Type + /// West /// - public enum CartesianType - { - /// - /// Spherical Cartesian - /// - Cartesian, - /// - /// Earth Centered Earth Fixed - /// - ECEF, - } - /// - /// Used for easy read math functions - /// - [Serializable] - internal static class ModM - { - public static double Mod(double x, double y) - { - return x - y * Math.Floor(x / y); - } + W + } - public static double ModLon(double x) - { - return Mod(x + Math.PI, 2 * Math.PI) - Math.PI; - } - public static double ModCrs(double x) - { - return Mod(x, 2 * Math.PI); - } - - public static double ModLat(double x) - { - return Mod(x + Math.PI / 2, 2 * Math.PI) - Math.PI / 2; - } - } + /// + /// Coordinate type datum specification + /// + [Serializable] + [Flags] + public enum Coordinate_Datum { /// - /// Earth Shape for Calculations. + /// Lat Long GeoDetic /// + LAT_LONG = 1, + /// + /// UTM and MGRS + /// + UTM_MGRS = 2, + /// + /// ECEF + /// + ECEF = 4, + } + + /// + /// Cartesian Coordinate Type + /// + public enum CartesianType { + /// + /// Spherical Cartesian + /// + Cartesian, + /// + /// Earth Centered Earth Fixed + /// + ECEF, + } + /// + /// Used for easy read math functions + /// + [Serializable] + internal static class ModM { + public static Double Mod(Double x, Double y) => x - y * Math.Floor(x / y); + + public static Double ModLon(Double x) => Mod(x + Math.PI, 2 * Math.PI) - Math.PI; + + public static Double ModCrs(Double x) => Mod(x, 2 * Math.PI); + + public static Double ModLat(Double x) => Mod(x + Math.PI / 2, 2 * Math.PI) - Math.PI / 2; + } + /// + /// Earth Shape for Calculations. + /// + + [Serializable] + public enum Shape { + /// + /// Calculate as sphere (less accurate, more efficient). + /// + Sphere, + /// + /// Calculate as ellipsoid (more accurate, less efficient). + /// + Ellipsoid + } - [Serializable] - public enum Shape - { - /// - /// Calculate as sphere (less accurate, more efficient). - /// - Sphere, - /// - /// Calculate as ellipsoid (more accurate, less efficient). - /// - Ellipsoid - } - } diff --git a/CoordinateSharp/Coordinate.Cartesian.cs b/CoordinateSharp/Coordinate.Cartesian.cs index 4dc981c..7b8ce51 100644 --- a/CoordinateSharp/Coordinate.Cartesian.cs +++ b/CoordinateSharp/Coordinate.Cartesian.cs @@ -1,155 +1,135 @@ using System; using System.ComponentModel; -namespace CoordinateSharp -{ + +namespace CoordinateSharp { + /// + /// Cartesian (X, Y, Z) Coordinate + /// + [Serializable] + public class Cartesian : INotifyPropertyChanged { /// - /// Cartesian (X, Y, Z) Coordinate + /// Create a Cartesian Object /// - [Serializable] - public class Cartesian : INotifyPropertyChanged - { - /// - /// Create a Cartesian Object - /// - /// - public Cartesian(Coordinate c) - { - //formulas: - x = Math.Cos(c.Latitude.ToRadians()) * Math.Cos(c.Longitude.ToRadians()); - y = Math.Cos(c.Latitude.ToRadians()) * Math.Sin(c.Longitude.ToRadians()); - z = Math.Sin(c.Latitude.ToRadians()); - } - /// - /// Create a Cartesian Object - /// - /// X - /// Y - /// Z - public Cartesian(double xc, double yc, double zc) - { - //formulas: - x = xc; - y = yc; - z = zc; - } - /// - /// Updates Cartesian Values - /// - /// - public void ToCartesian(Coordinate c) - { - x = Math.Cos(c.Latitude.ToRadians()) * Math.Cos(c.Longitude.ToRadians()); - y = Math.Cos(c.Latitude.ToRadians()) * Math.Sin(c.Longitude.ToRadians()); - z = Math.Sin(c.Latitude.ToRadians()); - } - private double x; - private double y; - private double z; - - /// - /// X Coordinate - /// - public double X - { - get { return x; } - set - { - if(x != value) - { - x = value; - NotifyPropertyChanged("X"); - } - } - } - /// - /// y Coordinate - /// - public double Y - { - get { return y; } - set - { - if (y != value) - { - y = value; - NotifyPropertyChanged("Y"); - } - } - } - /// - /// Z Coordinate - /// - public double Z - { - get { return z; } - set - { - if (z != value) - { - z = value; - NotifyPropertyChanged("Z"); - } - } - } - /// - /// Returns a Lat Long Coordinate object based on the provided Cartesian Coordinate - /// - /// X - /// Y - /// Z - /// - public static Coordinate CartesianToLatLong(double x, double y, double z) - { - double lon = Math.Atan2(y, x); - double hyp = Math.Sqrt(x * x + y * y); - double lat = Math.Atan2(z, hyp); - - double Lat = lat * (180 / Math.PI); - double Lon = lon * (180 / Math.PI); - return new Coordinate(Lat, Lon); - } - /// - /// Returns a Lat Long Coordinate object based on the provided Cartesian Coordinate - /// - /// Cartesian Coordinate - /// - public static Coordinate CartesianToLatLong(Cartesian cart) - { - double x = cart.X; - double y = cart.Y; - double z = cart.Z; - - double lon = Math.Atan2(y, x); - double hyp = Math.Sqrt(x * x + y * y); - double lat = Math.Atan2(z, hyp); - - double Lat = lat * (180 / Math.PI); - double Lon = lon * (180 / Math.PI); - return new Coordinate(Lat, Lon); - } - /// - /// Cartesian Default String Format - /// - /// Cartesian Formatted Coordinate String - /// Values rounded to the 8th place - public override string ToString() - { - return Math.Round(x,8).ToString() + " " + Math.Round(y, 8).ToString() + " " + Math.Round(z, 8).ToString(); - } - /// - /// Property changed event - /// - public event PropertyChangedEventHandler PropertyChanged; - /// - /// Notify property changed - /// - /// Property name - public void NotifyPropertyChanged(string propName) - { - if (this.PropertyChanged != null) - { - PropertyChanged(this, new PropertyChangedEventArgs(propName)); - } - } + /// + public Cartesian(Coordinate c) { + //formulas: + this.x = Math.Cos(c.Latitude.ToRadians()) * Math.Cos(c.Longitude.ToRadians()); + this.y = Math.Cos(c.Latitude.ToRadians()) * Math.Sin(c.Longitude.ToRadians()); + this.z = Math.Sin(c.Latitude.ToRadians()); } + /// + /// Create a Cartesian Object + /// + /// X + /// Y + /// Z + public Cartesian(Double xc, Double yc, Double zc) { + //formulas: + this.x = xc; + this.y = yc; + this.z = zc; + } + /// + /// Updates Cartesian Values + /// + /// + public void ToCartesian(Coordinate c) { + this.x = Math.Cos(c.Latitude.ToRadians()) * Math.Cos(c.Longitude.ToRadians()); + this.y = Math.Cos(c.Latitude.ToRadians()) * Math.Sin(c.Longitude.ToRadians()); + this.z = Math.Sin(c.Latitude.ToRadians()); + } + private Double x; + private Double y; + private Double z; + + /// + /// X Coordinate + /// + public Double X { + get => this.x; + set { + if (this.x != value) { + this.x = value; + this.NotifyPropertyChanged("X"); + } + } + } + /// + /// y Coordinate + /// + public Double Y { + get => this.y; + set { + if (this.y != value) { + this.y = value; + this.NotifyPropertyChanged("Y"); + } + } + } + /// + /// Z Coordinate + /// + public Double Z { + get => this.z; + set { + if (this.z != value) { + this.z = value; + this.NotifyPropertyChanged("Z"); + } + } + } + /// + /// Returns a Lat Long Coordinate object based on the provided Cartesian Coordinate + /// + /// X + /// Y + /// Z + /// + public static Coordinate CartesianToLatLong(Double x, Double y, Double z) { + Double lon = Math.Atan2(y, x); + Double hyp = Math.Sqrt(x * x + y * y); + Double lat = Math.Atan2(z, hyp); + + Double Lat = lat * (180 / Math.PI); + Double Lon = lon * (180 / Math.PI); + return new Coordinate(Lat, Lon); + } + /// + /// Returns a Lat Long Coordinate object based on the provided Cartesian Coordinate + /// + /// Cartesian Coordinate + /// + public static Coordinate CartesianToLatLong(Cartesian cart) { + Double x = cart.X; + Double y = cart.Y; + Double z = cart.Z; + + Double lon = Math.Atan2(y, x); + Double hyp = Math.Sqrt(x * x + y * y); + Double lat = Math.Atan2(z, hyp); + + Double Lat = lat * (180 / Math.PI); + Double Lon = lon * (180 / Math.PI); + return new Coordinate(Lat, Lon); + } + /// + /// Cartesian Default String Format + /// + /// Cartesian Formatted Coordinate String + /// Values rounded to the 8th place + public override String ToString() => Math.Round(this.x, 8).ToString() + " " + Math.Round(this.y, 8).ToString() + " " + Math.Round(this.z, 8).ToString(); + /// + /// Property changed event + /// + public event PropertyChangedEventHandler PropertyChanged; + /// + /// Notify property changed + /// + /// Property name + public void NotifyPropertyChanged(String propName) { + if (this.PropertyChanged != null) { + PropertyChanged(this, new PropertyChangedEventArgs(propName)); + } + } + } } diff --git a/CoordinateSharp/Coordinate.ECEF.cs b/CoordinateSharp/Coordinate.ECEF.cs index 86da2c9..8e789f0 100644 --- a/CoordinateSharp/Coordinate.ECEF.cs +++ b/CoordinateSharp/Coordinate.ECEF.cs @@ -1,566 +1,515 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using System.ComponentModel; -namespace CoordinateSharp -{ + +namespace CoordinateSharp { + /// + /// Earth Centered - Earth Fixed (X,Y,Z) Coordinate + /// + [Serializable] + public class ECEF : INotifyPropertyChanged { /// - /// Earth Centered - Earth Fixed (X,Y,Z) Coordinate + /// Create an ECEF Object /// - [Serializable] - public class ECEF : INotifyPropertyChanged - { - /// - /// Create an ECEF Object - /// - /// Coordinate - public ECEF(Coordinate c) - { - equatorial_radius = 6378137.0; - inverse_flattening = 298.257223563; - WGS84(); - geodetic_height = new Distance(0); - double[] ecef = LatLong_To_ECEF(c.Latitude.DecimalDegree, c.Longitude.DecimalDegree, geodetic_height.Kilometers); - x = ecef[0]; - y = ecef[1]; - z = ecef[2]; - } - /// - /// Create an ECEF Object - /// - /// Coordinate - /// Coordinate - public ECEF(Coordinate c, Distance height) - { - equatorial_radius = 6378137.0; - inverse_flattening = 298.257223563; - WGS84(); - geodetic_height = height; - double[] ecef = LatLong_To_ECEF(c.Latitude.DecimalDegree, c.Longitude.DecimalDegree, geodetic_height.Kilometers); - x = ecef[0]; - y = ecef[1]; - z = ecef[2]; - } - /// - /// Create an ECEF Object - /// - /// X - /// Y - /// Z - public ECEF(double xc, double yc, double zc) - { - equatorial_radius = 6378137.0; - inverse_flattening = 298.257223563; - WGS84(); - geodetic_height = new Distance(0); - x = xc; - y = yc; - z = zc; - } - /// - /// Updates ECEF Values - /// - /// Coordinate - public void ToECEF(Coordinate c) - { - equatorial_radius = 6378137.0; - inverse_flattening = 298.257223563; - WGS84(); - double[] ecef = LatLong_To_ECEF(c.Latitude.DecimalDegree, c.Longitude.DecimalDegree, geodetic_height.Kilometers); - x = ecef[0]; - y = ecef[1]; - z = ecef[2]; - } - - //Globals for calucations - private double EARTH_A; - private double EARTH_B; - private double EARTH_F; - private double EARTH_Ecc; - private double EARTH_Esq; - - //ECEF Values - private double x; - private double y; - private double z; - private Distance geodetic_height; - - //Datum - internal double equatorial_radius; - internal double inverse_flattening; - - /// - /// Datum Equatorial Radius / Semi Major Axis - /// - public double Equatorial_Radius - { - get { return equatorial_radius; } - } - - /// - /// Datum Flattening - /// - public double Inverse_Flattening - { - get { return inverse_flattening; } - } - - /// - /// X Coordinate - /// - public double X - { - get { return x; } - set - { - if (x != value) - { - x = value; - NotifyPropertyChanged("X"); - } - } - } - /// - /// y Coordinate - /// - public double Y - { - get { return y; } - set - { - if (y != value) - { - y = value; - NotifyPropertyChanged("Y"); - } - } - } - /// - /// Z Coordinate - /// - public double Z - { - get { return z; } - set - { - if (z != value) - { - z = value; - NotifyPropertyChanged("Z"); - } - } - } - - /// - /// GeoDetic Height from Mean Sea Level. - /// Used for converting Lat Long / ECEF. - /// Default value is 0. Adjust as needed. - /// - public Distance GeoDetic_Height - { - get { return geodetic_height; } - internal set - { - if (geodetic_height != value) - { - geodetic_height = value; - NotifyPropertyChanged("Height"); - - } - } - } - - /// - /// Sets GeoDetic height for ECEF conversion. - /// Recalculate ECEF Coordinate - /// - /// Coordinate - /// Height - public void Set_GeoDetic_Height(Coordinate c, Distance dist) - { - geodetic_height = dist; - double[] values = LatLong_To_ECEF(c.Latitude.DecimalDegree, c.Longitude.DecimalDegree, dist.Kilometers); - x = values[0]; - y = values[1]; - z = values[2]; - - } - - /// - /// Returns a Geodetic Coordinate object based on the provided ECEF Coordinate - /// - /// X - /// Y - /// Z - /// Coordinate - public static Coordinate ECEFToLatLong(double x, double y, double z) - { - ECEF ecef = new ECEF(x, y, z); - double[] values = ecef.ECEF_To_LatLong(x, y, z); - ecef.geodetic_height =new Distance(values[2]); - - Coordinate c = new Coordinate(values[0], values[1]); - c.ECEF = ecef; - return c; - } - /// - /// Returns a Geodetic Coordinate object based on the provided ECEF Coordinate - /// - /// ECEF Coordinate - /// Coordinate - public static Coordinate ECEFToLatLong(ECEF ecef) - { - double[] values = ecef.ECEF_To_LatLong(ecef.X, ecef.Y, ecef.Z); - - Coordinate c = new Coordinate(values[0], values[1]); - Distance height = new Distance(values[2]); - - ecef.geodetic_height = new Distance(values[2]); - c.ECEF = ecef; - - return c; - } - /// - /// ECEF Default String Format - /// - /// ECEF Formatted Coordinate String - /// Values rounded to the 3rd place - public override string ToString() - { - return Math.Round(x, 3).ToString() + " km, " + Math.Round(y, 3).ToString() + " km, " + Math.Round(z, 3).ToString() + " km"; - } - - /// - /// Property changed event - /// - public event PropertyChangedEventHandler PropertyChanged; - /// - /// Notify property changed - /// - /// Property name - public void NotifyPropertyChanged(string propName) - { - if (PropertyChanged != null) - { - PropertyChanged(this, new PropertyChangedEventArgs(propName)); - } - } - - //CONVERSION LOGIC - /// - /// Initialize EARTH global variables based on the Datum - /// - private void WGS84() - { - double wgs84a = equatorial_radius / 1000; - double wgs84f = 1.0 / inverse_flattening; - double wgs84b = wgs84a * (1.0 - wgs84f); - - EarthCon(wgs84a, wgs84b); - } - - /// - /// Sets Earth Constants as Globals - /// - /// a - /// b - private void EarthCon(double a, double b) - { - double f = 1 - b / a; - double eccsq = 1 - b * b / (a * a); - double ecc = Math.Sqrt(eccsq); - - EARTH_A = a; - EARTH_B = b; - EARTH_F = f; - EARTH_Ecc = ecc; - EARTH_Esq = eccsq; - } - - /// - /// Compute the radii at the geodetic latitude (degrees) - /// - /// Latitude in degres - /// double[] - private double[] radcur(double lat) - { - double[] rrnrm = new double[3]; - - double dtr = Math.PI / 180.0; - - double a = EARTH_A; - double b = EARTH_B; - - double asq = a * a; - double bsq = b * b; - double eccsq = 1 - bsq / asq; - double ecc = Math.Sqrt(eccsq); - - double clat = Math.Cos(dtr * lat); - double slat = Math.Sin(dtr * lat); - - double dsq = 1.0 - eccsq * slat * slat; - double d = Math.Sqrt(dsq); - - double rn = a / d; - double rm = rn * (1.0 - eccsq) / dsq; - - double rho = rn * clat; - double z = (1.0 - eccsq) * rn * slat; - double rsq = rho * rho + z * z; - double r = Math.Sqrt(rsq); - - rrnrm[0] = r; - rrnrm[1] = rn; - rrnrm[2] = rm; - - return (rrnrm); - - } - - /// - /// Physical radius of the Earth - /// - /// Latidude in degrees - /// double - private double rearth(double lat) - { - double[] rrnrm; - rrnrm = radcur(lat); - double r = rrnrm[0]; - - return r; - } - - /// - /// Converts geocentric latitude to geodetic latitude - /// - /// Geocentric latitude - /// Altitude in KM - /// double - private double gc2gd(double flatgc, double altkm) - { - var dtr = Math.PI / 180.0; - var rtd = 1 / dtr; - - double ecc = EARTH_Ecc; - double esq = ecc * ecc; - - //approximation by stages - //1st use gc-lat as if is gd, then correct alt dependence - - double altnow = altkm; - - double[] rrnrm = radcur(flatgc); - double rn = rrnrm[1]; - - double ratio = 1 - esq * rn / (rn + altnow); - - double tlat = Math.Tan(dtr * flatgc) / ratio; - double flatgd = rtd * Math.Atan(tlat); - - //now use this approximation for gd-lat to get rn etc. - - rrnrm = radcur(flatgd); - rn = rrnrm[1]; - - ratio = 1 - esq * rn / (rn + altnow); - tlat = Math.Tan(dtr * flatgc) / ratio; - flatgd = rtd * Math.Atan(tlat); - - return flatgd; - } - - /// - /// Converts geodetic latitude to geocentric latitude - /// - /// Geodetic latitude tp geocentric latitide - /// Altitude in KM - /// double - private double gd2gc(double flatgd, double altkm) - { - double dtr = Math.PI / 180.0; - double rtd = 1 / dtr; - - double ecc = EARTH_Ecc; - double esq = ecc * ecc; - - double altnow = altkm; - - double[] rrnrm = radcur(flatgd); - double rn = rrnrm[1]; - - double ratio = 1 - esq * rn / (rn + altnow); - - double tlat = Math.Tan(dtr * flatgd) * ratio; - double flatgc = rtd * Math.Atan(tlat); - - return flatgc; - } - - /// - /// Converts lat / long to east, north, up vectors - /// - /// Latitude - /// Longitude - /// Array[] of double[] - private Array[] llenu(double flat, double flon) - { - double clat, slat, clon, slon; - double[] ee = new double[3]; - double[] en = new double[3]; - double[] eu = new double[3]; - - Array[] enu = new Array[3]; - - double dtr = Math.PI / 180.0; - - clat = Math.Cos(dtr * flat); - slat = Math.Sin(dtr * flat); - clon = Math.Cos(dtr * flon); - slon = Math.Sin(dtr * flon); - - ee[0] = -slon; - ee[1] = clon; - ee[2] = 0.0; - - en[0] = -clon * slat; - en[1] = -slon * slat; - en[2] = clat; - - eu[0] = clon * clat; - eu[1] = slon * clat; - eu[2] = slat; - - enu[0] = ee; - enu[1] = en; - enu[2] = eu; - - return enu; - } - - /// - /// Gets ECEF vector in KM - /// - /// Latitude - /// Longitude - /// Altitude in KM - /// double[] - private double[] LatLong_To_ECEF(double lat, double longi, double altkm) - { - double dtr = Math.PI / 180.0; - - double clat = Math.Cos(dtr * lat); - double slat = Math.Sin(dtr * lat); - double clon = Math.Cos(dtr * longi); - double slon = Math.Sin(dtr * longi); - - double[] rrnrm = radcur(lat); - double rn = rrnrm[1]; - double re = rrnrm[0]; - - double ecc = EARTH_Ecc; - double esq = ecc * ecc; - - double x = (rn + altkm) * clat * clon; - double y = (rn + altkm) * clat * slon; - double z = ((1 - esq) * rn + altkm) * slat; - - double[] xvec = new double[3]; - - xvec[0] = x; - xvec[1] = y; - xvec[2] = z; - - return xvec; - } - - /// - /// Converts ECEF X, Y, Z to GeoDetic Lat / Long and Height in KM - /// - /// - /// - /// - /// - private double[] ECEF_To_LatLong(double x, double y, double z) - { - var dtr = Math.PI / 180.0; - - double[] rrnrm = new double[3]; - double[] llhvec = new double[3]; - double slat, tangd, flatn, dlat, clat; - double flat; - double altkm; - - double esq = EARTH_Esq; - - double rp = Math.Sqrt(x * x + y * y + z * z); - - double flatgc = Math.Asin(z / rp) / dtr; - double flon; - double testval = Math.Abs(x) + Math.Abs(y); - if (testval < 1.0e-10) - { flon = 0.0; } - else - { flon = Math.Atan2(y, x) / dtr; } - if (flon < 0.0) { flon = flon + 360.0; } - - double p = Math.Sqrt(x * x + y * y); - - //Pole special case - - if (p < 1.0e-10) - { - flat = 90.0; - if (z < 0.0) { flat = -90.0; } - - altkm = rp - rearth(flat); - llhvec[0] = flat; - llhvec[1] = flon; - llhvec[2] = altkm; - - return llhvec; - } - - //first iteration, use flatgc to get altitude - //and alt needed to convert gc to gd lat. - - double rnow = rearth(flatgc); - altkm = rp - rnow; - flat = gc2gd(flatgc, altkm); - - rrnrm = radcur(flat); - double rn = rrnrm[1]; - - for (int kount = 0; kount < 5; kount++) - { - slat = Math.Sin(dtr * flat); - tangd = (z + rn * esq * slat) / p; - flatn = Math.Atan(tangd) / dtr; - - dlat = flatn - flat; - flat = flatn; - clat = Math.Cos(dtr * flat); - - rrnrm = radcur(flat); - rn = rrnrm[1]; - - altkm = (p / clat) - rn; - - if (Math.Abs(dlat) < 1.0e-12) { break; } - - } - //CONVERTER WORKS IN E LAT ONLY, IF E LAT > 180 LAT IS WEST SO IT MUCST BE CONVERTED TO Decimal - - if (flon > 180) { flon = flon - 360; } - llhvec[0] = flat; - llhvec[1] = flon; - llhvec[2] = altkm; - - return llhvec; - } - - + /// Coordinate + public ECEF(Coordinate c) { + this.equatorial_radius = 6378137.0; + this.inverse_flattening = 298.257223563; + this.WGS84(); + this.geodetic_height = new Distance(0); + Double[] ecef = this.LatLong_To_ECEF(c.Latitude.DecimalDegree, c.Longitude.DecimalDegree, this.geodetic_height.Kilometers); + this.x = ecef[0]; + this.y = ecef[1]; + this.z = ecef[2]; } + /// + /// Create an ECEF Object + /// + /// Coordinate + /// Coordinate + public ECEF(Coordinate c, Distance height) { + this.equatorial_radius = 6378137.0; + this.inverse_flattening = 298.257223563; + this.WGS84(); + this.geodetic_height = height; + Double[] ecef = this.LatLong_To_ECEF(c.Latitude.DecimalDegree, c.Longitude.DecimalDegree, this.geodetic_height.Kilometers); + this.x = ecef[0]; + this.y = ecef[1]; + this.z = ecef[2]; + } + /// + /// Create an ECEF Object + /// + /// X + /// Y + /// Z + public ECEF(Double xc, Double yc, Double zc) { + this.equatorial_radius = 6378137.0; + this.inverse_flattening = 298.257223563; + this.WGS84(); + this.geodetic_height = new Distance(0); + this.x = xc; + this.y = yc; + this.z = zc; + } + /// + /// Updates ECEF Values + /// + /// Coordinate + public void ToECEF(Coordinate c) { + this.equatorial_radius = 6378137.0; + this.inverse_flattening = 298.257223563; + this.WGS84(); + Double[] ecef = this.LatLong_To_ECEF(c.Latitude.DecimalDegree, c.Longitude.DecimalDegree, this.geodetic_height.Kilometers); + this.x = ecef[0]; + this.y = ecef[1]; + this.z = ecef[2]; + } + + //Globals for calucations + private Double EARTH_A; + private Double EARTH_B; + //private Double EARTH_F; + private Double EARTH_Ecc; + private Double EARTH_Esq; + + //ECEF Values + private Double x; + private Double y; + private Double z; + private Distance geodetic_height; + + //Datum + internal Double equatorial_radius; + internal Double inverse_flattening; + + /// + /// Datum Equatorial Radius / Semi Major Axis + /// + public Double Equatorial_Radius => this.equatorial_radius; + + /// + /// Datum Flattening + /// + public Double Inverse_Flattening => this.inverse_flattening; + + /// + /// X Coordinate + /// + public Double X { + get => this.x; + set { + if (this.x != value) { + this.x = value; + this.NotifyPropertyChanged("X"); + } + } + } + /// + /// y Coordinate + /// + public Double Y { + get => this.y; + set { + if (this.y != value) { + this.y = value; + this.NotifyPropertyChanged("Y"); + } + } + } + /// + /// Z Coordinate + /// + public Double Z { + get => this.z; + set { + if (this.z != value) { + this.z = value; + this.NotifyPropertyChanged("Z"); + } + } + } + + /// + /// GeoDetic Height from Mean Sea Level. + /// Used for converting Lat Long / ECEF. + /// Default value is 0. Adjust as needed. + /// + public Distance GeoDetic_Height { + get => this.geodetic_height; + internal set { + if (this.geodetic_height != value) { + this.geodetic_height = value; + this.NotifyPropertyChanged("Height"); + + } + } + } + + /// + /// Sets GeoDetic height for ECEF conversion. + /// Recalculate ECEF Coordinate + /// + /// Coordinate + /// Height + public void Set_GeoDetic_Height(Coordinate c, Distance dist) { + this.geodetic_height = dist; + Double[] values = this.LatLong_To_ECEF(c.Latitude.DecimalDegree, c.Longitude.DecimalDegree, dist.Kilometers); + this.x = values[0]; + this.y = values[1]; + this.z = values[2]; + + } + + /// + /// Returns a Geodetic Coordinate object based on the provided ECEF Coordinate + /// + /// X + /// Y + /// Z + /// Coordinate + public static Coordinate ECEFToLatLong(Double x, Double y, Double z) { + ECEF ecef = new ECEF(x, y, z); + Double[] values = ecef.ECEF_To_LatLong(x, y, z); + ecef.geodetic_height = new Distance(values[2]); + + Coordinate c = new Coordinate(values[0], values[1]) { + ECEF = ecef + }; + return c; + } + /// + /// Returns a Geodetic Coordinate object based on the provided ECEF Coordinate + /// + /// ECEF Coordinate + /// Coordinate + public static Coordinate ECEFToLatLong(ECEF ecef) { + Double[] values = ecef.ECEF_To_LatLong(ecef.X, ecef.Y, ecef.Z); + + Coordinate c = new Coordinate(values[0], values[1]); + //Distance height = new Distance(values[2]); + + ecef.geodetic_height = new Distance(values[2]); + c.ECEF = ecef; + + return c; + } + /// + /// ECEF Default String Format + /// + /// ECEF Formatted Coordinate String + /// Values rounded to the 3rd place + public override String ToString() => Math.Round(this.x, 3).ToString() + " km, " + Math.Round(this.y, 3).ToString() + " km, " + Math.Round(this.z, 3).ToString() + " km"; + + /// + /// Property changed event + /// + public event PropertyChangedEventHandler PropertyChanged; + /// + /// Notify property changed + /// + /// Property name + public void NotifyPropertyChanged(String propName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName)); + + //CONVERSION LOGIC + /// + /// Initialize EARTH global variables based on the Datum + /// + private void WGS84() { + Double wgs84a = this.equatorial_radius / 1000; + Double wgs84f = 1.0 / this.inverse_flattening; + Double wgs84b = wgs84a * (1.0 - wgs84f); + + this.EarthCon(wgs84a, wgs84b); + } + + /// + /// Sets Earth Constants as Globals + /// + /// a + /// b + private void EarthCon(Double a, Double b) { + //Double f = 1 - b / a; + Double eccsq = 1 - b * b / (a * a); + Double ecc = Math.Sqrt(eccsq); + + this.EARTH_A = a; + this.EARTH_B = b; + //this.EARTH_F = f; + this.EARTH_Ecc = ecc; + this.EARTH_Esq = eccsq; + } + + /// + /// Compute the radii at the geodetic latitude (degrees) + /// + /// Latitude in degres + /// double[] + private Double[] Radcur(Double lat) { + Double[] rrnrm = new Double[3]; + + Double dtr = Math.PI / 180.0; + + Double a = this.EARTH_A; + Double b = this.EARTH_B; + + Double asq = a * a; + Double bsq = b * b; + Double eccsq = 1 - bsq / asq; + //Double ecc = Math.Sqrt(eccsq); + + Double clat = Math.Cos(dtr * lat); + Double slat = Math.Sin(dtr * lat); + + Double dsq = 1.0 - eccsq * slat * slat; + Double d = Math.Sqrt(dsq); + + Double rn = a / d; + Double rm = rn * (1.0 - eccsq) / dsq; + + Double rho = rn * clat; + Double z = (1.0 - eccsq) * rn * slat; + Double rsq = rho * rho + z * z; + Double r = Math.Sqrt(rsq); + + rrnrm[0] = r; + rrnrm[1] = rn; + rrnrm[2] = rm; + + return rrnrm; + + } + + /// + /// Physical radius of the Earth + /// + /// Latidude in degrees + /// double + private Double Rearth(Double lat) { + Double[] rrnrm; + rrnrm = this.Radcur(lat); + Double r = rrnrm[0]; + + return r; + } + + /// + /// Converts geocentric latitude to geodetic latitude + /// + /// Geocentric latitude + /// Altitude in KM + /// double + private Double Gc2gd(Double flatgc, Double altkm) { + Double dtr = Math.PI / 180.0; + Double rtd = 1 / dtr; + + Double ecc = this.EARTH_Ecc; + Double esq = ecc * ecc; + + //approximation by stages + //1st use gc-lat as if is gd, then correct alt dependence + + Double altnow = altkm; + + Double[] rrnrm = this.Radcur(flatgc); + Double rn = rrnrm[1]; + + Double ratio = 1 - esq * rn / (rn + altnow); + + Double tlat = Math.Tan(dtr * flatgc) / ratio; + Double flatgd = rtd * Math.Atan(tlat); + + //now use this approximation for gd-lat to get rn etc. + + rrnrm = this.Radcur(flatgd); + rn = rrnrm[1]; + + ratio = 1 - esq * rn / (rn + altnow); + tlat = Math.Tan(dtr * flatgc) / ratio; + flatgd = rtd * Math.Atan(tlat); + + return flatgd; + } + + /// + /// Converts geodetic latitude to geocentric latitude + /// + /// Geodetic latitude tp geocentric latitide + /// Altitude in KM + /// double + /*private Double gd2gc(Double flatgd, Double altkm) { + Double dtr = Math.PI / 180.0; + Double rtd = 1 / dtr; + + Double ecc = this.EARTH_Ecc; + Double esq = ecc * ecc; + + Double altnow = altkm; + + Double[] rrnrm = this.Radcur(flatgd); + Double rn = rrnrm[1]; + + Double ratio = 1 - esq * rn / (rn + altnow); + + Double tlat = Math.Tan(dtr * flatgd) * ratio; + Double flatgc = rtd * Math.Atan(tlat); + + return flatgc; + }*/ + + /// + /// Converts lat / long to east, north, up vectors + /// + /// Latitude + /// Longitude + /// Array[] of double[] + /*private Array[] llenu(Double flat, Double flon) { + Double clat, slat, clon, slon; + Double[] ee = new Double[3]; + Double[] en = new Double[3]; + Double[] eu = new Double[3]; + + Array[] enu = new Array[3]; + + Double dtr = Math.PI / 180.0; + + clat = Math.Cos(dtr * flat); + slat = Math.Sin(dtr * flat); + clon = Math.Cos(dtr * flon); + slon = Math.Sin(dtr * flon); + + ee[0] = -slon; + ee[1] = clon; + ee[2] = 0.0; + + en[0] = -clon * slat; + en[1] = -slon * slat; + en[2] = clat; + + eu[0] = clon * clat; + eu[1] = slon * clat; + eu[2] = slat; + + enu[0] = ee; + enu[1] = en; + enu[2] = eu; + + return enu; + }*/ + + /// + /// Gets ECEF vector in KM + /// + /// Latitude + /// Longitude + /// Altitude in KM + /// double[] + private Double[] LatLong_To_ECEF(Double lat, Double longi, Double altkm) { + Double dtr = Math.PI / 180.0; + + Double clat = Math.Cos(dtr * lat); + Double slat = Math.Sin(dtr * lat); + Double clon = Math.Cos(dtr * longi); + Double slon = Math.Sin(dtr * longi); + + Double[] rrnrm = this.Radcur(lat); + Double rn = rrnrm[1]; + //Double re = rrnrm[0]; + + Double ecc = this.EARTH_Ecc; + Double esq = ecc * ecc; + + Double x = (rn + altkm) * clat * clon; + Double y = (rn + altkm) * clat * slon; + Double z = ((1 - esq) * rn + altkm) * slat; + + Double[] xvec = new Double[3]; + + xvec[0] = x; + xvec[1] = y; + xvec[2] = z; + + return xvec; + } + + /// + /// Converts ECEF X, Y, Z to GeoDetic Lat / Long and Height in KM + /// + /// + /// + /// + /// + private Double[] ECEF_To_LatLong(Double x, Double y, Double z) { + Double dtr = Math.PI / 180.0; + + //_ = new Double[3]; + Double[] llhvec = new Double[3]; + Double slat, tangd, flatn, dlat, clat; + Double flat; + Double altkm; + + Double esq = this.EARTH_Esq; + + Double rp = Math.Sqrt(x * x + y * y + z * z); + + Double flatgc = Math.Asin(z / rp) / dtr; + Double flon; + Double testval = Math.Abs(x) + Math.Abs(y); + flon = testval < 1.0e-10 ? 0.0 : Math.Atan2(y, x) / dtr; + if (flon < 0.0) { flon += 360.0; } + + Double p = Math.Sqrt(x * x + y * y); + + //Pole special case + + if (p < 1.0e-10) { + flat = 90.0; + if (z < 0.0) { flat = -90.0; } + + altkm = rp - this.Rearth(flat); + llhvec[0] = flat; + llhvec[1] = flon; + llhvec[2] = altkm; + + return llhvec; + } + + //first iteration, use flatgc to get altitude + //and alt needed to convert gc to gd lat. + + Double rnow = this.Rearth(flatgc); + altkm = rp - rnow; + flat = this.Gc2gd(flatgc, altkm); + + Double[] rrnrm = this.Radcur(flat); + Double rn = rrnrm[1]; + + for (Int32 kount = 0; kount < 5; kount++) { + slat = Math.Sin(dtr * flat); + tangd = (z + rn * esq * slat) / p; + flatn = Math.Atan(tangd) / dtr; + + dlat = flatn - flat; + flat = flatn; + clat = Math.Cos(dtr * flat); + + rrnrm = this.Radcur(flat); + rn = rrnrm[1]; + + altkm = p / clat - rn; + + if (Math.Abs(dlat) < 1.0e-12) { break; } + + } + //CONVERTER WORKS IN E LAT ONLY, IF E LAT > 180 LAT IS WEST SO IT MUCST BE CONVERTED TO Decimal + + if (flon > 180) { flon -= 360; } + llhvec[0] = flat; + llhvec[1] = flon; + llhvec[2] = altkm; + + return llhvec; + } + + + } } diff --git a/CoordinateSharp/Coordinate.EagerLoad.cs b/CoordinateSharp/Coordinate.EagerLoad.cs index f535a67..c15c074 100644 --- a/CoordinateSharp/Coordinate.EagerLoad.cs +++ b/CoordinateSharp/Coordinate.EagerLoad.cs @@ -1,119 +1,105 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -namespace CoordinateSharp -{ +namespace CoordinateSharp { + /// + /// Turn on/off eager loading of certain properties. + /// + [Serializable] + public class EagerLoad { /// - /// Turn on/off eager loading of certain properties. + /// Create an EagerLoad object /// - [Serializable] - public class EagerLoad - { - /// - /// Create an EagerLoad object - /// - public EagerLoad() - { - Celestial = true; - UTM_MGRS = true; - Cartesian = true; - ECEF = true; - } - - /// - /// Create an EagerLoad object with all options on or off - /// - /// Turns EagerLoad on or off - public EagerLoad(bool isOn) - { - Celestial = isOn; - UTM_MGRS = isOn; - Cartesian = isOn; - ECEF = isOn; - } - - /// - /// Create an EagerLoad object with only the specified flag options turned on. - /// - /// EagerLoadType - public EagerLoad(EagerLoadType et) - { - Cartesian = false; - Celestial = false; - UTM_MGRS = false; - ECEF = false; - - if (et.HasFlag(EagerLoadType.Cartesian)) - { - Cartesian = true; - } - if (et.HasFlag(EagerLoadType.Celestial)) - { - Celestial = true; - } - if (et.HasFlag(EagerLoadType.UTM_MGRS)) - { - UTM_MGRS = true; - } - if (et.HasFlag(EagerLoadType.ECEF)) - { - ECEF = true; - } - } - - /// - /// Creates an EagerLoad object. Only the specified flags will be set to EagerLoad. - /// - /// EagerLoadType - /// EagerLoad - public static EagerLoad Create(EagerLoadType et) - { - EagerLoad el = new EagerLoad(et); - return el; - } - - /// - /// Eager load celestial information. - /// - public bool Celestial { get; set; } - /// - /// Eager load UTM and MGRS information - /// - public bool UTM_MGRS { get; set; } - /// - /// Eager load Cartesian information - /// - public bool Cartesian { get; set; } - /// - /// Eager load ECEF information - /// - public bool ECEF { get; set; } + public EagerLoad() { + this.Celestial = true; + this.UTM_MGRS = true; + this.Cartesian = true; + this.ECEF = true; } + /// - /// EagerLoad Enumerator + /// Create an EagerLoad object with all options on or off /// - [Serializable] - [Flags] - public enum EagerLoadType - { - /// - /// UTM and MGRS - /// - UTM_MGRS = 1, - /// - /// Celestial - /// - Celestial = 2, - /// - /// Cartesian - /// - Cartesian = 4, - /// - /// ECEF - /// - ECEF = 8 - + /// Turns EagerLoad on or off + public EagerLoad(Boolean isOn) { + this.Celestial = isOn; + this.UTM_MGRS = isOn; + this.Cartesian = isOn; + this.ECEF = isOn; } + + /// + /// Create an EagerLoad object with only the specified flag options turned on. + /// + /// EagerLoadType + public EagerLoad(EagerLoadType et) { + this.Cartesian = false; + this.Celestial = false; + this.UTM_MGRS = false; + this.ECEF = false; + + if (et.HasFlag(EagerLoadType.Cartesian)) { + this.Cartesian = true; + } + if (et.HasFlag(EagerLoadType.Celestial)) { + this.Celestial = true; + } + if (et.HasFlag(EagerLoadType.UTM_MGRS)) { + this.UTM_MGRS = true; + } + if (et.HasFlag(EagerLoadType.ECEF)) { + this.ECEF = true; + } + } + + /// + /// Creates an EagerLoad object. Only the specified flags will be set to EagerLoad. + /// + /// EagerLoadType + /// EagerLoad + public static EagerLoad Create(EagerLoadType et) { + EagerLoad el = new EagerLoad(et); + return el; + } + + /// + /// Eager load celestial information. + /// + public Boolean Celestial { get; set; } + /// + /// Eager load UTM and MGRS information + /// + public Boolean UTM_MGRS { get; set; } + /// + /// Eager load Cartesian information + /// + public Boolean Cartesian { get; set; } + /// + /// Eager load ECEF information + /// + public Boolean ECEF { get; set; } + } + /// + /// EagerLoad Enumerator + /// + [Serializable] + [Flags] + public enum EagerLoadType { + /// + /// UTM and MGRS + /// + UTM_MGRS = 1, + /// + /// Celestial + /// + Celestial = 2, + /// + /// Cartesian + /// + Cartesian = 4, + /// + /// ECEF + /// + ECEF = 8 + + } } diff --git a/CoordinateSharp/Coordinate.Formatting.cs b/CoordinateSharp/Coordinate.Formatting.cs index 2a0084d..e1f300c 100644 --- a/CoordinateSharp/Coordinate.Formatting.cs +++ b/CoordinateSharp/Coordinate.Formatting.cs @@ -1,107 +1,100 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -namespace CoordinateSharp -{ +namespace CoordinateSharp { + /// + /// Coordinate formatting options for a Coordinate object. + /// + [Serializable] + public class CoordinateFormatOptions { /// - /// Coordinate formatting options for a Coordinate object. + /// Set default values with the constructor. /// - [Serializable] - public class CoordinateFormatOptions - { - /// - /// Set default values with the constructor. - /// - public CoordinateFormatOptions() - { - Format = CoordinateFormatType.Degree_Minutes_Seconds; - Round = 3; - Display_Leading_Zeros = false; - Display_Trailing_Zeros = false; - Display_Symbols = true; - Display_Degree_Symbol = true; - Display_Minute_Symbol = true; - Display_Seconds_Symbol = true; - Display_Hyphens = false; - Position_First = true; - } - /// - /// Coordinate format type. - /// - public CoordinateFormatType Format { get; set; } - /// - /// Rounds Coordinates to the set value. - /// - public int Round { get; set; } - /// - /// Displays leading zeros. - /// - public bool Display_Leading_Zeros { get; set; } - /// - /// Display trailing zeros. - /// - public bool Display_Trailing_Zeros { get; set; } - /// - /// Allow symbols to display. - /// - public bool Display_Symbols { get; set; } - /// - /// Display degree symbols. - /// - public bool Display_Degree_Symbol { get; set; } - /// - /// Display minute symbols. - /// - public bool Display_Minute_Symbol { get; set; } - /// - /// Display secons symbol. - /// - public bool Display_Seconds_Symbol { get; set; } - /// - /// Display hyphens between values. - /// - public bool Display_Hyphens { get; set; } - /// - /// Show coordinate position first. - /// Will show last if set 'false'. - /// - public bool Position_First { get; set; } + public CoordinateFormatOptions() { + this.Format = CoordinateFormatType.Degree_Minutes_Seconds; + this.Round = 3; + this.Display_Leading_Zeros = false; + this.Display_Trailing_Zeros = false; + this.Display_Symbols = true; + this.Display_Degree_Symbol = true; + this.Display_Minute_Symbol = true; + this.Display_Seconds_Symbol = true; + this.Display_Hyphens = false; + this.Position_First = true; } /// - /// Coordinate Format Types. + /// Coordinate format type. /// - [Serializable] - public enum CoordinateFormatType - { - /// - /// Decimal Degree Format - /// - /// - /// Example: N 40.456 W 75.456 - /// - Decimal_Degree, - /// - /// Decimal Degree Minutes Format - /// - /// - /// Example: N 40º 34.552' W 70º 45.408' - /// - Degree_Decimal_Minutes, - /// - /// Decimal Degree Minutes Format - /// - /// - /// Example: N 40º 34" 36.552' W 70º 45" 24.408' - /// - Degree_Minutes_Seconds, - /// - /// Decimal Format - /// - /// - /// Example: 40.57674 -70.46574 - /// - Decimal - } + public CoordinateFormatType Format { get; set; } + /// + /// Rounds Coordinates to the set value. + /// + public Int32 Round { get; set; } + /// + /// Displays leading zeros. + /// + public Boolean Display_Leading_Zeros { get; set; } + /// + /// Display trailing zeros. + /// + public Boolean Display_Trailing_Zeros { get; set; } + /// + /// Allow symbols to display. + /// + public Boolean Display_Symbols { get; set; } + /// + /// Display degree symbols. + /// + public Boolean Display_Degree_Symbol { get; set; } + /// + /// Display minute symbols. + /// + public Boolean Display_Minute_Symbol { get; set; } + /// + /// Display secons symbol. + /// + public Boolean Display_Seconds_Symbol { get; set; } + /// + /// Display hyphens between values. + /// + public Boolean Display_Hyphens { get; set; } + /// + /// Show coordinate position first. + /// Will show last if set 'false'. + /// + public Boolean Position_First { get; set; } + } + /// + /// Coordinate Format Types. + /// + [Serializable] + public enum CoordinateFormatType { + /// + /// Decimal Degree Format + /// + /// + /// Example: N 40.456 W 75.456 + /// + Decimal_Degree, + /// + /// Decimal Degree Minutes Format + /// + /// + /// Example: N 40º 34.552' W 70º 45.408' + /// + Degree_Decimal_Minutes, + /// + /// Decimal Degree Minutes Format + /// + /// + /// Example: N 40º 34" 36.552' W 70º 45" 24.408' + /// + Degree_Minutes_Seconds, + /// + /// Decimal Format + /// + /// + /// Example: 40.57674 -70.46574 + /// + Decimal + } } diff --git a/CoordinateSharp/Coordinate.MGRS.cs b/CoordinateSharp/Coordinate.MGRS.cs index d34c5f2..1ee22aa 100644 --- a/CoordinateSharp/Coordinate.MGRS.cs +++ b/CoordinateSharp/Coordinate.MGRS.cs @@ -1,293 +1,221 @@ using System; -using System.Linq; -using System.Diagnostics; using System.ComponentModel; +using System.Diagnostics; +using System.Linq; -namespace CoordinateSharp -{ +namespace CoordinateSharp { + /// + /// Military Grid Reference System (MGRS). Uses the WGS 84 Datum. + /// Relies upon values from the UniversalTransverseMercator class + /// + [Serializable] + public class MilitaryGridReferenceSystem : INotifyPropertyChanged { /// - /// Military Grid Reference System (MGRS). Uses the WGS 84 Datum. - /// Relies upon values from the UniversalTransverseMercator class + /// Create an MGRS object with WGS84 datum /// - [Serializable] - public class MilitaryGridReferenceSystem : INotifyPropertyChanged - { - /// - /// Create an MGRS object with WGS84 datum - /// - /// Lat Zone - /// Long Zone - /// Digraph - /// Easting - /// Northing - public MilitaryGridReferenceSystem(string latz, int longz, string d, double e, double n) - { - string digraphLettersE = "ABCDEFGHJKLMNPQRSTUVWXYZ"; - string digraphLettersN = "ABCDEFGHJKLMNPQRSTUV"; - if (longz < 1 || longz > 60) { Debug.WriteLine("Longitudinal zone out of range", "UTM longitudinal zones must be between 1-60."); } - if (!Verify_Lat_Zone(latz)) { throw new ArgumentException("Latitudinal zone invalid", "UTM latitudinal zone was unrecognized."); } - if (n < 0 || n > 10000000) { throw new ArgumentOutOfRangeException("Northing out of range", "Northing must be between 0-10,000,000."); } - if (d.Count() < 2 || d.Count() > 2) { throw new ArgumentException("Digraph invalid", "MGRS Digraph was unrecognized."); } - if (digraphLettersE.ToCharArray().ToList().Where(x => x.ToString() == d.ToUpper()[0].ToString()).Count() == 0) { throw new ArgumentException("Digraph invalid", "MGRS Digraph was unrecognized."); } - if (digraphLettersN.ToCharArray().ToList().Where(x => x.ToString() == d.ToUpper()[1].ToString()).Count() == 0) { throw new ArgumentException("Digraph invalid", "MGRS Digraph was unrecognized."); } - latZone = latz; - longZone = longz; - digraph = d; - easting = e; - northing = n; - //WGS84 - equatorialRadius = 6378137.0; - inverseFlattening = 298.257223563; - - } - /// - /// Create an MGRS object with custom datum - /// - /// Lat Zone - /// Long Zone - /// Digraph - /// Easting - /// Northing - /// Equatorial Radius - /// Inverse Flattening - public MilitaryGridReferenceSystem(string latz, int longz, string d, double e, double n,double rad, double flt) - { - string digraphLettersE = "ABCDEFGHJKLMNPQRSTUVWXYZ"; - string digraphLettersN = "ABCDEFGHJKLMNPQRSTUV"; - if (longz < 1 || longz > 60) { Debug.WriteLine("Longitudinal zone out of range", "UTM longitudinal zones must be between 1-60."); } - if (!Verify_Lat_Zone(latz)) { throw new ArgumentException("Latitudinal zone invalid", "UTM latitudinal zone was unrecognized."); } - if (n < 0 || n > 10000000) { throw new ArgumentOutOfRangeException("Northing out of range", "Northing must be between 0-10,000,000."); } - if (d.Count() < 2 || d.Count() > 2) { throw new ArgumentException("Digraph invalid", "MGRS Digraph was unrecognized."); } - if (digraphLettersE.ToCharArray().ToList().Where(x => x.ToString() == d.ToUpper()[0].ToString()).Count() == 0) { throw new ArgumentException("Digraph invalid", "MGRS Digraph was unrecognized."); } - if (digraphLettersN.ToCharArray().ToList().Where(x => x.ToString() == d.ToUpper()[1].ToString()).Count() == 0) { throw new ArgumentException("Digraph invalid", "MGRS Digraph was unrecognized."); } - latZone = latz; - longZone = longz; - digraph = d; - easting = e; - northing = n; - - equatorialRadius = rad; - inverseFlattening = flt; - } + /// Lat Zone + /// Long Zone + /// Digraph + /// Easting + /// Northing + public MilitaryGridReferenceSystem(String latz, Int32 longz, String d, Double e, Double n) { + String digraphLettersE = "ABCDEFGHJKLMNPQRSTUVWXYZ"; + String digraphLettersN = "ABCDEFGHJKLMNPQRSTUV"; + if (longz < 1 || longz > 60) { Debug.WriteLine("Longitudinal zone out of range", "UTM longitudinal zones must be between 1-60."); } + if (!this.Verify_Lat_Zone(latz)) { throw new ArgumentException("Latitudinal zone invalid", "UTM latitudinal zone was unrecognized."); } + if (n < 0 || n > 10000000) { throw new ArgumentOutOfRangeException("Northing out of range", "Northing must be between 0-10,000,000."); } + if (d.Count() < 2 || d.Count() > 2) { throw new ArgumentException("Digraph invalid", "MGRS Digraph was unrecognized."); } + if (digraphLettersE.ToCharArray().ToList().Where(x => x.ToString() == d.ToUpper()[0].ToString()).Count() == 0) { throw new ArgumentException("Digraph invalid", "MGRS Digraph was unrecognized."); } + if (digraphLettersN.ToCharArray().ToList().Where(x => x.ToString() == d.ToUpper()[1].ToString()).Count() == 0) { throw new ArgumentException("Digraph invalid", "MGRS Digraph was unrecognized."); } + this.LatZone = latz; + this.LongZone = longz; + this.Digraph = d; + this.Easting = e; + this.Northing = n; + //WGS84 + this.equatorialRadius = 6378137.0; + this.inverseFlattening = 298.257223563; - private double equatorialRadius; - private double inverseFlattening; - private string latZone; - private int longZone; - private double easting; - private double northing; - private string digraph; - - private bool withinCoordinateSystemBounds=true; - - private bool Verify_Lat_Zone(string l) - { - if (LatZones.longZongLetters.Where(x => x == l.ToUpper()).Count() != 1) - { - return false; - } - return true; - } - - - /// - /// MGRS Zone Letter - /// - public string LatZone - { - get { return latZone; } - - } - /// - /// MGRS Zone Number - /// - public int LongZone - { - get { return longZone; } - - } - /// - /// MGRS Easting - /// - public double Easting - { - get { return easting; } - - } - /// - /// MGRS Northing - /// - public double Northing - { - get { return northing; } - - } - /// - /// MGRS Digraph - /// - public string Digraph - { - get { return digraph; } - } - - /// - /// Is MGRS conversion within the coordinate system's accurate boundaries after conversion from Lat/Long. - /// - public bool WithinCoordinateSystemBounds - { - get { return withinCoordinateSystemBounds; } - } - - - internal MilitaryGridReferenceSystem(UniversalTransverseMercator utm) - { - ToMGRS(utm); - } - internal void ToMGRS(UniversalTransverseMercator utm) - { - Digraphs digraphs = new Digraphs(); - - string digraph1 = digraphs.getDigraph1(utm.LongZone, utm.Easting); - string digraph2 = digraphs.getDigraph2(utm.LongZone, utm.Northing); - - digraph = digraph1 + digraph2; - latZone = utm.LatZone; - longZone = utm.LongZone; - - //String easting = String.valueOf((int)_easting); - string e = ((int)utm.Easting).ToString(); - if (e.Length < 5) - { - e = "00000" + ((int)utm.Easting).ToString(); - } - e = e.Substring(e.Length - 5); - - easting = Convert.ToInt32(e); - - string n = ((int)utm.Northing).ToString(); - if (n.Length < 5) - { - n = "0000" + ((int)utm.Northing).ToString(); - } - n = n.Substring(n.Length - 5); - - northing = Convert.ToInt32(n); - equatorialRadius = utm.equatorial_radius; - inverseFlattening = utm.inverse_flattening; - - withinCoordinateSystemBounds = utm.WithinCoordinateSystemBounds; - } - - /// - /// Creates a Coordinate object from an MGRS/NATO UTM Coordinate - /// - /// MilitaryGridReferenceSystem - /// Coordinate object - public static Coordinate MGRStoLatLong(MilitaryGridReferenceSystem mgrs) - { - string latz = mgrs.LatZone; - string digraph = mgrs.Digraph; - - char eltr = digraph[0]; - char nltr = digraph[1]; - - string digraphLettersE = "ABCDEFGHJKLMNPQRSTUVWXYZ"; - string digraphLettersN = "ABCDEFGHJKLMNPQRSTUV"; - string digraphLettersAll=""; - for (int lt = 1; lt < 25; lt++) - { - digraphLettersAll += "ABCDEFGHJKLMNPQRSTUV"; - } - - var eidx = digraphLettersE.IndexOf(eltr); - var nidx = digraphLettersN.IndexOf(nltr); - if (mgrs.LongZone / 2.0 == Math.Floor(mgrs.LongZone / 2.0)) - { - nidx -= 5; // correction for even numbered zones - } - - var ebase = 100000 * (1 + eidx - 8 * Math.Floor(Convert.ToDouble(eidx) / 8)); - var latBand = digraphLettersE.IndexOf(latz); - var latBandLow = 8 * latBand - 96; - var latBandHigh = 8 * latBand - 88; - - if (latBand < 2) - { - latBandLow = -90; - latBandHigh = -80; - } - else if (latBand == 21) - { - latBandLow = 72; - latBandHigh = 84; - } - else if (latBand > 21) - { - latBandLow = 84; - latBandHigh = 90; - } - - var lowLetter = Math.Floor(100 + 1.11 * latBandLow); - var highLetter = Math.Round(100 + 1.11 * latBandHigh); - - string latBandLetters = null; - int l = Convert.ToInt32(lowLetter); - int h = Convert.ToInt32(highLetter); - if (mgrs.LongZone / 2.0 == Math.Floor(mgrs.LongZone / 2.0)) - { - latBandLetters = digraphLettersAll.Substring(l + 5, h + 5).ToString(); - } - else - { - latBandLetters = digraphLettersAll.Substring(l, h).ToString(); - } - var nbase = 100000 * (lowLetter + latBandLetters.IndexOf(nltr)); - //latBandLetters.IndexOf(nltr) value causing incorrect Northing below -80 - var x = ebase + mgrs.Easting; - var y = nbase + mgrs.Northing; - if (y > 10000000) - { - y = y - 10000000; - } - if (nbase >= 10000000) - { - y = nbase + mgrs.northing - 10000000; - } - - var southern = nbase < 10000000; - UniversalTransverseMercator utm = new UniversalTransverseMercator(mgrs.LatZone, mgrs.LongZone, x, y); - utm.equatorial_radius = mgrs.equatorialRadius; - utm.inverse_flattening = mgrs.inverseFlattening; - Coordinate c = UniversalTransverseMercator.ConvertUTMtoLatLong(utm); - - c.Set_Datum(mgrs.equatorialRadius, mgrs.inverseFlattening); - - return c; - } - - /// - /// MGRS Default String Format - /// - /// MGRS Formatted Coordinate String - public override string ToString() - { - if (!withinCoordinateSystemBounds) { return ""; }//MGRS Coordinate is outside its reliable boundaries. Return empty. - return longZone.ToString() + LatZone + " " + digraph + " " + ((int)easting).ToString("00000") + " " + ((int)northing).ToString("00000"); - } - /// - /// Property changed event - /// - public event PropertyChangedEventHandler PropertyChanged; - /// - /// Notify property changed - /// - /// Property name - public void NotifyPropertyChanged(string propName) - { - if (PropertyChanged != null) - { - PropertyChanged(this, new PropertyChangedEventArgs(propName)); - } - } } + /// + /// Create an MGRS object with custom datum + /// + /// Lat Zone + /// Long Zone + /// Digraph + /// Easting + /// Northing + /// Equatorial Radius + /// Inverse Flattening + public MilitaryGridReferenceSystem(String latz, Int32 longz, String d, Double e, Double n, Double rad, Double flt) { + String digraphLettersE = "ABCDEFGHJKLMNPQRSTUVWXYZ"; + String digraphLettersN = "ABCDEFGHJKLMNPQRSTUV"; + if (longz < 1 || longz > 60) { Debug.WriteLine("Longitudinal zone out of range", "UTM longitudinal zones must be between 1-60."); } + if (!this.Verify_Lat_Zone(latz)) { throw new ArgumentException("Latitudinal zone invalid", "UTM latitudinal zone was unrecognized."); } + if (n < 0 || n > 10000000) { throw new ArgumentOutOfRangeException("Northing out of range", "Northing must be between 0-10,000,000."); } + if (d.Count() < 2 || d.Count() > 2) { throw new ArgumentException("Digraph invalid", "MGRS Digraph was unrecognized."); } + if (digraphLettersE.ToCharArray().ToList().Where(x => x.ToString() == d.ToUpper()[0].ToString()).Count() == 0) { throw new ArgumentException("Digraph invalid", "MGRS Digraph was unrecognized."); } + if (digraphLettersN.ToCharArray().ToList().Where(x => x.ToString() == d.ToUpper()[1].ToString()).Count() == 0) { throw new ArgumentException("Digraph invalid", "MGRS Digraph was unrecognized."); } + this.LatZone = latz; + this.LongZone = longz; + this.Digraph = d; + this.Easting = e; + this.Northing = n; + + this.equatorialRadius = rad; + this.inverseFlattening = flt; + } + + private Double equatorialRadius; + private Double inverseFlattening; + + private Boolean Verify_Lat_Zone(String l) => LatZones.longZongLetters.Where(x => x == l.ToUpper()).Count() == 1; + + + /// + /// MGRS Zone Letter + /// + public String LatZone { get; private set; } + /// + /// MGRS Zone Number + /// + public Int32 LongZone { get; private set; } + /// + /// MGRS Easting + /// + public Double Easting { get; private set; } + /// + /// MGRS Northing + /// + public Double Northing { get; private set; } + /// + /// MGRS Digraph + /// + public String Digraph { get; private set; } + + /// + /// Is MGRS conversion within the coordinate system's accurate boundaries after conversion from Lat/Long. + /// + public Boolean WithinCoordinateSystemBounds { get; private set; } = true; + + + internal MilitaryGridReferenceSystem(UniversalTransverseMercator utm) => this.ToMGRS(utm); + internal void ToMGRS(UniversalTransverseMercator utm) { + Digraphs digraphs = new Digraphs(); + + String digraph1 = digraphs.GetDigraph1(utm.LongZone, utm.Easting); + String digraph2 = digraphs.GetDigraph2(utm.LongZone, utm.Northing); + + this.Digraph = digraph1 + digraph2; + this.LatZone = utm.LatZone; + this.LongZone = utm.LongZone; + + //String easting = String.valueOf((int)_easting); + String e = ((Int32)utm.Easting).ToString(); + if (e.Length < 5) { + e = "00000" + ((Int32)utm.Easting).ToString(); + } + e = e.Substring(e.Length - 5); + + this.Easting = Convert.ToInt32(e); + + String n = ((Int32)utm.Northing).ToString(); + if (n.Length < 5) { + n = "0000" + ((Int32)utm.Northing).ToString(); + } + n = n.Substring(n.Length - 5); + + this.Northing = Convert.ToInt32(n); + this.equatorialRadius = utm.equatorial_radius; + this.inverseFlattening = utm.inverse_flattening; + + this.WithinCoordinateSystemBounds = utm.WithinCoordinateSystemBounds; + } + + /// + /// Creates a Coordinate object from an MGRS/NATO UTM Coordinate + /// + /// MilitaryGridReferenceSystem + /// Coordinate object + public static Coordinate MGRStoLatLong(MilitaryGridReferenceSystem mgrs) { + String latz = mgrs.LatZone; + String digraph = mgrs.Digraph; + + Char eltr = digraph[0]; + Char nltr = digraph[1]; + + String digraphLettersE = "ABCDEFGHJKLMNPQRSTUVWXYZ"; + //String digraphLettersN = "ABCDEFGHJKLMNPQRSTUV"; + String digraphLettersAll = ""; + for (Int32 lt = 1; lt < 25; lt++) { + digraphLettersAll += "ABCDEFGHJKLMNPQRSTUV"; + } + + Int32 eidx = digraphLettersE.IndexOf(eltr); + //Int32 nidx = digraphLettersN.IndexOf(nltr); + if (mgrs.LongZone / 2.0 == Math.Floor(mgrs.LongZone / 2.0)) { + //nidx -= 5; // correction for even numbered zones + } + + Double ebase = 100000 * (1 + eidx - 8 * Math.Floor(Convert.ToDouble(eidx) / 8)); + Int32 latBand = digraphLettersE.IndexOf(latz); + Int32 latBandLow = 8 * latBand - 96; + Int32 latBandHigh = 8 * latBand - 88; + + if (latBand < 2) { + latBandLow = -90; + latBandHigh = -80; + } else if (latBand == 21) { + latBandLow = 72; + latBandHigh = 84; + } else if (latBand > 21) { + latBandLow = 84; + latBandHigh = 90; + } + + Double lowLetter = Math.Floor(100 + 1.11 * latBandLow); + Double highLetter = Math.Round(100 + 1.11 * latBandHigh); + Int32 l = Convert.ToInt32(lowLetter); + Int32 h = Convert.ToInt32(highLetter); + + String latBandLetters = mgrs.LongZone / 2.0 == Math.Floor(mgrs.LongZone / 2.0) ? digraphLettersAll.Substring(l + 5, h + 5).ToString() : digraphLettersAll.Substring(l, h).ToString(); + Double nbase = 100000 * (lowLetter + latBandLetters.IndexOf(nltr)); + //latBandLetters.IndexOf(nltr) value causing incorrect Northing below -80 + Double x = ebase + mgrs.Easting; + Double y = nbase + mgrs.Northing; + if (y > 10000000) { + y -= 10000000; + } + if (nbase >= 10000000) { + y = nbase + mgrs.Northing - 10000000; + } + + _ = nbase < 10000000; + UniversalTransverseMercator utm = new UniversalTransverseMercator(mgrs.LatZone, mgrs.LongZone, x, y) { + equatorial_radius = mgrs.equatorialRadius, + inverse_flattening = mgrs.inverseFlattening + }; + Coordinate c = UniversalTransverseMercator.ConvertUTMtoLatLong(utm); + + c.Set_Datum(mgrs.equatorialRadius, mgrs.inverseFlattening); + + return c; + } + + /// + /// MGRS Default String Format + /// + /// MGRS Formatted Coordinate String + public override String ToString() => !this.WithinCoordinateSystemBounds ? "" : this.LongZone.ToString() + this.LatZone + " " + this.Digraph + " " + ((Int32)this.Easting).ToString("00000") + " " + ((Int32)this.Northing).ToString("00000"); + //MGRS Coordinate is outside its reliable boundaries. Return empty. + /// + /// Property changed event + /// + public event PropertyChangedEventHandler PropertyChanged; + /// + /// Notify property changed + /// + /// Property name + public void NotifyPropertyChanged(String propName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName)); + } } diff --git a/CoordinateSharp/Coordinate.Parser.cs b/CoordinateSharp/Coordinate.Parser.cs index 21daad0..f698213 100644 --- a/CoordinateSharp/Coordinate.Parser.cs +++ b/CoordinateSharp/Coordinate.Parser.cs @@ -1,925 +1,722 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Text; using System.Text.RegularExpressions; -namespace CoordinateSharp -{ + +namespace CoordinateSharp { + /// + /// Type of format a Coordinate parsed from. + /// + [Serializable] + public enum Parse_Format_Type { /// - /// Type of format a Coordinate parsed from. + /// Coordinate was not initialized from a parser method. /// - [Serializable] - public enum Parse_Format_Type - { - /// - /// Coordinate was not initialized from a parser method. - /// - None, - /// - /// Signed Degree - /// DD.dddd - /// - Signed_Degree, - /// - /// Decimal Degree - /// P DD.dddd - /// - Decimal_Degree, - /// - /// Degree Decimal Minute - /// P DD MM.sss - /// - Degree_Decimal_Minute, - /// - /// Degree Minute Second - /// P DD MM SS.sss - /// - Degree_Minute_Second, - /// - /// Universal Transverse Mercator - /// - UTM, - /// - /// Military Grid Reference System - /// - MGRS, - /// - /// Spherical Cartesian - /// - Cartesian_Spherical, - /// - /// Earth Centered Earth Fixed - /// - Cartesian_ECEF + None, + /// + /// Signed Degree + /// DD.dddd + /// + Signed_Degree, + /// + /// Decimal Degree + /// P DD.dddd + /// + Decimal_Degree, + /// + /// Degree Decimal Minute + /// P DD MM.sss + /// + Degree_Decimal_Minute, + /// + /// Degree Minute Second + /// P DD MM SS.sss + /// + Degree_Minute_Second, + /// + /// Universal Transverse Mercator + /// + UTM, + /// + /// Military Grid Reference System + /// + MGRS, + /// + /// Spherical Cartesian + /// + Cartesian_Spherical, + /// + /// Earth Centered Earth Fixed + /// + Cartesian_ECEF + } + + internal class FormatFinder { + //Add main to Coordinate and tunnel to Format class. Add private methods to format. + //WHEN PARSING NO EXCPETIONS FOR OUT OF RANGE ARGS WILL BE THROWN + public static Boolean TryParse(String coordString, CartesianType ct, out Coordinate c) { + //Turn of eagerload for efficiency + EagerLoad eg = new EagerLoad { + Cartesian = false, + Celestial = false, + UTM_MGRS = false + }; + + _ = new Coordinate(eg); + String s = coordString; + s = s.Trim(); //Trim all spaces before and after string + //Try Signed Degree + if (TrySignedDegree(s, out Double[] d)) { + try { + c = new Coordinate(d[0], d[1], eg) { + Parse_Format = Parse_Format_Type.Signed_Degree + }; + return true; + } catch {//Parser failed try next method + } + } + + //Try Decimal Degree + if (TryDecimalDegree(s, out d)) { + try { + c = new Coordinate(d[0], d[1], eg) { + Parse_Format = Parse_Format_Type.Decimal_Degree + }; + return true; + } catch {//Parser failed try next method + } + } + //Try DDM + if (TryDegreeDecimalMinute(s, out d)) { + try { + //0 Lat Degree + //1 Lat Minute + //2 Lat Direction (0 = N, 1 = S) + //3 Long Degree + //4 Long Minute + //5 Long Direction (0 = E, 1 = W) + CoordinatesPosition latP = CoordinatesPosition.N; + CoordinatesPosition lngP = CoordinatesPosition.E; + if (d[2] != 0) { latP = CoordinatesPosition.S; } + if (d[5] != 0) { lngP = CoordinatesPosition.W; } + CoordinatePart lat = new CoordinatePart((Int32)d[0], d[1], latP); + CoordinatePart lng = new CoordinatePart((Int32)d[3], d[4], lngP); + c = new Coordinate(eg) { + Latitude = lat, + Longitude = lng, + Parse_Format = Parse_Format_Type.Degree_Decimal_Minute + }; + return true; + } catch {//Parser failed try next method + } + } + //Try DMS + if (TryDegreeMinuteSecond(s, out d)) { + try { + //0 Lat Degree + //1 Lat Minute + //2 Lat Second + //3 Lat Direction (0 = N, 1 = S) + //4 Long Degree + //5 Long Minute + //6 Long Second + //7 Long Direction (0 = E, 1 = W) + CoordinatesPosition latP = CoordinatesPosition.N; + CoordinatesPosition lngP = CoordinatesPosition.E; + if (d[3] != 0) { latP = CoordinatesPosition.S; } + if (d[7] != 0) { lngP = CoordinatesPosition.W; } + + CoordinatePart lat = new CoordinatePart((Int32)d[0], (Int32)d[1], d[2], latP); + CoordinatePart lng = new CoordinatePart((Int32)d[4], (Int32)d[5], d[6], lngP); + c = new Coordinate(eg) { + Latitude = lat, + Longitude = lng, + Parse_Format = Parse_Format_Type.Degree_Minute_Second + }; + return true; + } catch {//Parser failed try next method + } + } + + //Try MGRS + if (TryMGRS(s, out String[] um)) { + try { + Double zone = Convert.ToDouble(um[0]); + Double easting = Convert.ToDouble(um[3]); + Double northing = Convert.ToDouble(um[4]); + MilitaryGridReferenceSystem mgrs = new MilitaryGridReferenceSystem(um[1], (Int32)zone, um[2], easting, northing); + c = MilitaryGridReferenceSystem.MGRStoLatLong(mgrs); + c.Parse_Format = Parse_Format_Type.MGRS; + return true; + } catch {//Parser failed try next method + } + } + //Try UTM + if (TryUTM(s, out um)) { + try { + Double zone = Convert.ToDouble(um[0]); + Double easting = Convert.ToDouble(um[2]); + Double northing = Convert.ToDouble(um[3]); + UniversalTransverseMercator utm = new UniversalTransverseMercator(um[1], (Int32)zone, easting, northing); + c = UniversalTransverseMercator.ConvertUTMtoLatLong(utm); + c.Parse_Format = Parse_Format_Type.UTM; + return true; + } catch {//Parser failed try next method + } + } + //Try Cartesian + if (TryCartesian(s.ToUpper().Replace("KM", "").Replace("X", "").Replace("Y", "").Replace("Z", ""), out d)) { + if (ct == CartesianType.Cartesian) { + try { + Cartesian cart = new Cartesian(d[0], d[1], d[2]); + c = Cartesian.CartesianToLatLong(cart); + c.Parse_Format = Parse_Format_Type.Cartesian_Spherical; + return true; + } catch {//Parser failed try next method + } + } + if (ct == CartesianType.ECEF) { + try { + ECEF ecef = new ECEF(d[0], d[1], d[2]); + c = ECEF.ECEFToLatLong(ecef); + c.Parse_Format = Parse_Format_Type.Cartesian_ECEF; + return true; + } catch {//Parser failed try next method + } + } + } + + c = null; + return false; + } + private static Boolean TrySignedDegree(String s, out Double[] d) { + d = null; + if (Regex.Matches(s, @"[a-zA-Z]").Count != 0) { return false; } //Should contain no letters + + String[] sA = SpecialSplit(s, false); + Double lat; + Double lng; + + Double degLat; + Double minLat; //Minutes & MinSeconds + Double secLat; + + Int32 signLat = 1; + + Double degLng; + Double minLng; //Minutes & MinSeconds + Double secLng; + + Int32 signLng = 1; + + switch (sA.Count()) { + case 2: + if (!Double.TryParse(sA[0], out lat)) { return false; } + if (!Double.TryParse(sA[1], out lng)) { return false; } + d = new Double[] { lat, lng }; + return true; + case 4: + if (!Double.TryParse(sA[0], out degLat)) { return false; } + if (!Double.TryParse(sA[1], out minLat)) { return false; } + if (!Double.TryParse(sA[2], out degLng)) { return false; } + if (!Double.TryParse(sA[3], out minLng)) { return false; } + + if (degLat < 0) { signLat = -1; } + if (degLng < 0) { signLng = -1; } + if (minLat >= 60 || minLat < 0) { return false; } //Handle in parser as degree will be incorrect. + if (minLng >= 60 || minLng < 0) { return false; } //Handle in parser as degree will be incorrect. + lat = (Math.Abs(degLat) + minLat / 60.0) * signLat; + lng = (Math.Abs(degLng) + minLng / 60.0) * signLng; + d = new Double[] { lat, lng }; + return true; + case 6: + if (!Double.TryParse(sA[0], out degLat)) { return false; } + if (!Double.TryParse(sA[1], out minLat)) { return false; } + if (!Double.TryParse(sA[2], out secLat)) { return false; } + if (!Double.TryParse(sA[3], out degLng)) { return false; } + if (!Double.TryParse(sA[4], out minLng)) { return false; } + if (!Double.TryParse(sA[5], out secLng)) { return false; } + if (degLat < 0) { signLat = -1; } + if (degLng < 0) { signLng = -1; } + if (minLat >= 60 || minLat < 0) { return false; } //Handle in parser as degree will be incorrect. + if (minLng >= 60 || minLng < 0) { return false; } //Handle in parser as degree will be incorrect. + if (secLat >= 60 || secLat < 0) { return false; } //Handle in parser as degree will be incorrect. + if (secLng >= 60 || secLng < 0) { return false; } //Handle in parser as degree will be incorrect. + lat = (Math.Abs(degLat) + minLat / 60.0 + secLat / 3600) * signLat; + lng = (Math.Abs(degLng) + minLng / 60.0 + secLng / 3600) * signLng; + d = new Double[] { lat, lng }; + return true; + default: + return false; + } + } + private static Boolean TryDecimalDegree(String s, out Double[] d) { + d = null; + if (Regex.Matches(s, @"[a-zA-Z]").Count != 2) { return false; } //Should only contain 1 letter. + + String[] sA = SpecialSplit(s, true); + if (sA.Count() == 2 || sA.Count() == 4) { + + Double latR = 1; //Sets negative if South + Double lngR = 1; //Sets negative if West + + //Contact get brin directional indicator together with string + if (sA.Count() == 4) { + sA[0] += sA[1]; + sA[1] = sA[2] + sA[3]; + } + + //Find Directions + if (!sA[0].Contains("N") && !sA[0].Contains("n")) { + if (!sA[0].Contains("S") && !sA[0].Contains("s")) { + return false;//No Direction Found + } + latR = -1; + } + if (!sA[1].Contains("E") && !sA[1].Contains("e")) { + if (!sA[1].Contains("W") && !sA[1].Contains("w")) { + return false;//No Direction Found + } + lngR = -1; + } + + sA[0] = Regex.Replace(sA[0], "[^0-9.]", ""); + sA[1] = Regex.Replace(sA[1], "[^0-9.]", ""); + + if (!Double.TryParse(sA[0], out Double lat)) { return false; } + if (!Double.TryParse(sA[1], out Double lng)) { return false; } + lat *= latR; + lng *= lngR; + d = new Double[] { lat, lng }; + return true; + } + + return false; + } + private static Boolean TryDegreeDecimalMinute(String s, out Double[] d) { + d = null; + if (Regex.Matches(s, @"[a-zA-Z]").Count != 2) { return false; } //Should only contain 1 letter. + + String[] sA = SpecialSplit(s, true); + if (sA.Count() == 4 || sA.Count() == 6) { + + Double latR = 0; //Sets 1 if South + Double lngR = 0; //Sets 1 if West + + //Contact get in order to combine directional indicator together with string + //Should reduce 6 items to 4 + if (sA.Count() == 6) { + if (Char.IsLetter(sA[0][0])) { sA[0] += sA[1]; sA[1] = sA[2]; } else if (Char.IsLetter(sA[1][0])) { sA[0] += sA[1]; sA[1] = sA[2]; } else if (Char.IsLetter(sA[2][0])) { sA[0] += sA[2]; } else { return false; } + + if (Char.IsLetter(sA[3][0])) { sA[3] += sA[4]; sA[4] = sA[5]; } else if (Char.IsLetter(sA[4][0])) { sA[3] += sA[4]; sA[4] = sA[5]; } else if (Char.IsLetter(sA[5][0])) { sA[3] += sA[5]; } else { return false; } + + //Shift values for below logic + sA[2] = sA[3]; + sA[3] = sA[4]; + } + + //Find Directions + if (!sA[0].Contains("N") && !sA[0].Contains("n") && !sA[1].Contains("N") && !sA[1].Contains("n")) { + if (!sA[0].Contains("S") && !sA[0].Contains("s") && !sA[1].Contains("S") && !sA[1].Contains("s")) { + return false;//No Direction Found + } + latR = 1; + } + if (!sA[2].Contains("E") && !sA[2].Contains("e") && !sA[3].Contains("E") && !sA[3].Contains("e")) { + if (!sA[2].Contains("W") && !sA[2].Contains("w") && !sA[3].Contains("W") && !sA[3].Contains("w")) { + return false;//No Direction Found + } + lngR = 1; + } + + sA[0] = Regex.Replace(sA[0], "[^0-9.]", ""); + sA[1] = Regex.Replace(sA[1], "[^0-9.]", ""); + sA[2] = Regex.Replace(sA[2], "[^0-9.]", ""); + sA[3] = Regex.Replace(sA[3], "[^0-9.]", ""); + + if (!Double.TryParse(sA[0], out Double latD)) { return false; } + if (!Double.TryParse(sA[1], out Double latMS)) { return false; } + if (!Double.TryParse(sA[2], out Double lngD)) { return false; } + if (!Double.TryParse(sA[3], out Double lngMS)) { return false; } + + d = new Double[] { latD, latMS, latR, lngD, lngMS, lngR }; + return true; + } + return false; + } + private static Boolean TryDegreeMinuteSecond(String s, out Double[] d) { + d = null; + if (Regex.Matches(s, @"[a-zA-Z]").Count != 2) { return false; } //Should only contain 1 letter. + + String[] sA = SpecialSplit(s, true); + if (sA.Count() == 6 || sA.Count() == 8) { + + Double latR = 0; //Sets 1 if South + Double lngR = 0; //Sets 1 if West + + //Contact get in order to combine directional indicator together with string + //Should reduce 8 items to 6 + if (sA.Count() == 8) { + if (Char.IsLetter(sA[0][0])) { sA[0] += sA[1]; sA[1] = sA[2]; sA[2] = sA[3]; } else if (Char.IsLetter(sA[1][0])) { sA[0] += sA[1]; sA[1] = sA[2]; sA[2] = sA[3]; } else if (Char.IsLetter(sA[3][0])) { sA[0] += sA[3]; } else { return false; } + + if (Char.IsLetter(sA[4][0])) { sA[4] += sA[5]; sA[5] = sA[6]; sA[6] = sA[7]; } else if (Char.IsLetter(sA[5][0])) { sA[4] += sA[5]; sA[5] = sA[6]; sA[6] = sA[7]; } else if (Char.IsLetter(sA[7][0])) { sA[4] += sA[7]; } else { return false; } + + //Shift values for below logic + sA[3] = sA[4]; + sA[4] = sA[5]; + sA[5] = sA[6]; + } + + //Find Directions + if (!sA[0].Contains("N") && !sA[0].Contains("n") && !sA[2].Contains("N") && !sA[2].Contains("n")) { + if (!sA[0].Contains("S") && !sA[0].Contains("s") && !sA[2].Contains("S") && !sA[2].Contains("s")) { + return false;//No Direction Found + } + latR = 1; + } + if (!sA[3].Contains("E") && !sA[3].Contains("e") && !sA[5].Contains("E") && !sA[5].Contains("e")) { + if (!sA[3].Contains("W") && !sA[3].Contains("w") && !sA[5].Contains("W") && !sA[5].Contains("w")) { + return false;//No Direction Found + } + lngR = 1; + } + sA[0] = Regex.Replace(sA[0], "[^0-9.]", ""); + sA[1] = Regex.Replace(sA[1], "[^0-9.]", ""); + sA[2] = Regex.Replace(sA[2], "[^0-9.]", ""); + sA[3] = Regex.Replace(sA[3], "[^0-9.]", ""); + sA[4] = Regex.Replace(sA[4], "[^0-9.]", ""); + sA[5] = Regex.Replace(sA[5], "[^0-9.]", ""); + + if (!Double.TryParse(sA[0], out Double latD)) { return false; } + if (!Double.TryParse(sA[1], out Double latM)) { return false; } + if (!Double.TryParse(sA[2], out Double latS)) { return false; } + if (!Double.TryParse(sA[3], out Double lngD)) { return false; } + if (!Double.TryParse(sA[4], out Double lngM)) { return false; } + if (!Double.TryParse(sA[5], out Double lngS)) { return false; } + + d = new Double[] { latD, latM, latS, latR, lngD, lngM, lngS, lngR }; + return true; + } + return false; + } + private static Boolean TryUTM(String s, out String[] utm) { + utm = null; + String[] sA = SpecialSplit(s, false); + if (sA.Count() == 3 || sA.Count() == 4) { + String zoneL; + + if (sA.Count() == 4) { + + if (Char.IsLetter(sA[0][0])) { sA[0] += sA[1]; sA[1] = sA[2]; sA[2] = sA[3]; } else if (Char.IsLetter(sA[1][0])) { sA[0] += sA[1]; sA[1] = sA[2]; sA[2] = sA[3]; } else { return false; } + } + zoneL = new String(sA[0].Where(Char.IsLetter).ToArray()); + if (zoneL == String.Empty) { return false; } + sA[0] = Regex.Replace(sA[0], "[^0-9.]", ""); + + if (!Double.TryParse(sA[0], out Double zone)) { return false; } + if (!Double.TryParse(sA[1], out Double easting)) { return false; } + if (!Double.TryParse(sA[2], out Double northing)) { return false; } + + utm = new String[] { zone.ToString(), zoneL, easting.ToString(), northing.ToString() }; + return true; + } + return false; + } + private static Boolean TryMGRS(String s, out String[] mgrs) { + mgrs = null; + String[] sA = SpecialSplit(s, false); + if (sA.Count() == 4 || sA.Count() == 5) { + String zoneL; + String diagraph; + + if (sA.Count() == 5) { + if (Char.IsLetter(sA[0][0])) { sA[0] += sA[1]; sA[1] = sA[2]; sA[2] = sA[3]; } else if (Char.IsLetter(sA[1][0])) { sA[0] += sA[1]; sA[1] = sA[2]; sA[2] = sA[3]; } else { return false; } + } + zoneL = new String(sA[0].Where(Char.IsLetter).ToArray()); + if (zoneL == String.Empty) { return false; } + sA[0] = Regex.Replace(sA[0], "[^0-9.]", ""); + diagraph = sA[1]; + if (!Double.TryParse(sA[0], out Double zone)) { return false; } + if (!Double.TryParse(sA[2], out Double easting)) { return false; } + if (!Double.TryParse(sA[3], out Double northing)) { return false; } + + mgrs = new String[] { zone.ToString(), zoneL, diagraph, easting.ToString(), northing.ToString() }; + return true; + } + return false; + } + private static Boolean TryCartesian(String s, out Double[] d) { + d = null; + String[] sA = SpecialSplit(s, false); + + if (sA.Count() == 3) { + if (!Double.TryParse(sA[0], out Double x)) { return false; } + if (!Double.TryParse(sA[1], out Double y)) { return false; } + if (!Double.TryParse(sA[2], out Double z)) { return false; } + d = new Double[] { x, y, z }; + return true; + } + return false; } - internal class FormatFinder - { - //Add main to Coordinate and tunnel to Format class. Add private methods to format. - //WHEN PARSING NO EXCPETIONS FOR OUT OF RANGE ARGS WILL BE THROWN - public static bool TryParse(string coordString, CartesianType ct, out Coordinate c) - { - //Turn of eagerload for efficiency - EagerLoad eg = new EagerLoad(); - eg.Cartesian = false; - eg.Celestial = false; - eg.UTM_MGRS = false; - - c = new Coordinate(eg); - string s = coordString; - s = s.Trim(); //Trim all spaces before and after string - double[] d; - //Try Signed Degree - if (TrySignedDegree(s, out d)) - { - try - { - c = new Coordinate(d[0], d[1], eg); - c.Parse_Format = Parse_Format_Type.Signed_Degree; - return true; - } - catch - {//Parser failed try next method - } - } - - //Try Decimal Degree - if (TryDecimalDegree(s, out d)) - { - try - { - c = new Coordinate(d[0], d[1], eg); - c.Parse_Format = Parse_Format_Type.Decimal_Degree; - return true; - } - catch - {//Parser failed try next method - } - } - //Try DDM - if (TryDegreeDecimalMinute(s, out d)) - { - try - { - //0 Lat Degree - //1 Lat Minute - //2 Lat Direction (0 = N, 1 = S) - //3 Long Degree - //4 Long Minute - //5 Long Direction (0 = E, 1 = W) - CoordinatesPosition latP = CoordinatesPosition.N; - CoordinatesPosition lngP = CoordinatesPosition.E; - if (d[2] != 0) { latP = CoordinatesPosition.S; } - if (d[5] != 0) { lngP = CoordinatesPosition.W; } - CoordinatePart lat = new CoordinatePart((int)d[0], d[1], latP); - CoordinatePart lng = new CoordinatePart((int)d[3], d[4], lngP); - c = new Coordinate(eg); - c.Latitude = lat; - c.Longitude = lng; - c.Parse_Format = Parse_Format_Type.Degree_Decimal_Minute; - return true; - } - catch - {//Parser failed try next method - } - } - //Try DMS - if (TryDegreeMinuteSecond(s, out d)) - { - try - { - //0 Lat Degree - //1 Lat Minute - //2 Lat Second - //3 Lat Direction (0 = N, 1 = S) - //4 Long Degree - //5 Long Minute - //6 Long Second - //7 Long Direction (0 = E, 1 = W) - CoordinatesPosition latP = CoordinatesPosition.N; - CoordinatesPosition lngP = CoordinatesPosition.E; - if (d[3] != 0) { latP = CoordinatesPosition.S; } - if (d[7] != 0) { lngP = CoordinatesPosition.W; } - - CoordinatePart lat = new CoordinatePart((int)d[0], (int)d[1], d[2], latP); - CoordinatePart lng = new CoordinatePart((int)d[4], (int)d[5], d[6], lngP); - c = new Coordinate(eg); - c.Latitude = lat; - c.Longitude = lng; - c.Parse_Format = Parse_Format_Type.Degree_Minute_Second; - return true; - } - catch - {//Parser failed try next method - } - } - - string[] um; - //Try MGRS - if (TryMGRS(s, out um)) - { - try - { - double zone = Convert.ToDouble(um[0]); - double easting = Convert.ToDouble(um[3]); - double northing = Convert.ToDouble(um[4]); - MilitaryGridReferenceSystem mgrs = new MilitaryGridReferenceSystem(um[1], (int)zone, um[2], easting, northing); - c = MilitaryGridReferenceSystem.MGRStoLatLong(mgrs); - c.Parse_Format = Parse_Format_Type.MGRS; - return true; - } - catch - {//Parser failed try next method - } - } - //Try UTM - if (TryUTM(s, out um)) - { - try - { - double zone = Convert.ToDouble(um[0]); - double easting = Convert.ToDouble(um[2]); - double northing = Convert.ToDouble(um[3]); - UniversalTransverseMercator utm = new UniversalTransverseMercator(um[1], (int)zone, easting, northing); - c = UniversalTransverseMercator.ConvertUTMtoLatLong(utm); - c.Parse_Format = Parse_Format_Type.UTM; - return true; - } - catch - {//Parser failed try next method - } - } - //Try Cartesian - if (TryCartesian(s.ToUpper().Replace("KM", "").Replace("X","").Replace("Y", "").Replace("Z", ""), out d)) - { - if (ct == CartesianType.Cartesian) - { - try - { - Cartesian cart = new Cartesian(d[0], d[1], d[2]); - c = Cartesian.CartesianToLatLong(cart); - c.Parse_Format = Parse_Format_Type.Cartesian_Spherical; - return true; - } - catch - {//Parser failed try next method - } - } - if (ct == CartesianType.ECEF) - { - try - { - ECEF ecef = new ECEF(d[0], d[1], d[2]); - c = ECEF.ECEFToLatLong(ecef); - c.Parse_Format = Parse_Format_Type.Cartesian_ECEF; - return true; - } - catch - {//Parser failed try next method - } - } - } - - c = null; - return false; - } - private static bool TrySignedDegree(string s, out double[] d) - { - d = null; - if (Regex.Matches(s, @"[a-zA-Z]").Count != 0) { return false; } //Should contain no letters - - string[] sA = SpecialSplit(s,false); - double lat; - double lng; - - double degLat; - double minLat; //Minutes & MinSeconds - double secLat; - - int signLat = 1; - - double degLng; - double minLng; //Minutes & MinSeconds - double secLng; - - int signLng = 1; - - switch (sA.Count()) - { - case 2: - if (!double.TryParse(sA[0], out lat)) - { return false; } - if (!double.TryParse(sA[1], out lng)) - { return false; } - d = new double[] { lat, lng }; - return true; - case 4: - if (!double.TryParse(sA[0], out degLat)) - { return false; } - if (!double.TryParse(sA[1], out minLat)) - { return false; } - if (!double.TryParse(sA[2], out degLng)) - { return false; } - if (!double.TryParse(sA[3], out minLng)) - { return false; } - - if (degLat < 0) { signLat = -1; } - if (degLng < 0) { signLng = -1; } - if (minLat >= 60 || minLat < 0) { return false; } //Handle in parser as degree will be incorrect. - if (minLng >= 60 || minLng < 0) { return false; } //Handle in parser as degree will be incorrect. - lat = (Math.Abs(degLat) + (minLat / 60.0)) * signLat; - lng = (Math.Abs(degLng) + (minLng / 60.0)) * signLng; - d = new double[] { lat, lng }; - return true; - case 6: - if (!double.TryParse(sA[0], out degLat)) - { return false; } - if (!double.TryParse(sA[1], out minLat)) - { return false; } - if (!double.TryParse(sA[2], out secLat)) - { return false; } - if (!double.TryParse(sA[3], out degLng)) - { return false; } - if (!double.TryParse(sA[4], out minLng)) - { return false; } - if (!double.TryParse(sA[5], out secLng)) - { return false; } - if (degLat < 0) { signLat = -1; } - if (degLng < 0) { signLng = -1; } - if (minLat >= 60 || minLat < 0) { return false; } //Handle in parser as degree will be incorrect. - if (minLng >= 60 || minLng < 0) { return false; } //Handle in parser as degree will be incorrect. - if (secLat >= 60 || secLat < 0) { return false; } //Handle in parser as degree will be incorrect. - if (secLng >= 60 || secLng < 0) { return false; } //Handle in parser as degree will be incorrect. - lat = (Math.Abs(degLat) + (minLat / 60.0) + (secLat / 3600)) * signLat; - lng = (Math.Abs(degLng) + (minLng / 60.0) + (secLng / 3600)) * signLng; - d = new double[] { lat, lng }; - return true; - default: - return false; - } - } - private static bool TryDecimalDegree(string s, out double[] d) - { - d = null; - if (Regex.Matches(s, @"[a-zA-Z]").Count != 2) { return false; } //Should only contain 1 letter. - - string[] sA = SpecialSplit(s,true); - if (sA.Count() == 2 || sA.Count() == 4) - { - double lat; - double lng; - - double latR = 1; //Sets negative if South - double lngR = 1; //Sets negative if West - - //Contact get brin directional indicator together with string - if (sA.Count() == 4) - { - sA[0] += sA[1]; - sA[1] = sA[2] + sA[3]; - } - - //Find Directions - if (!sA[0].Contains("N") && !sA[0].Contains("n")) - { - if (!sA[0].Contains("S") && !sA[0].Contains("s")) - { - return false;//No Direction Found - } - latR = -1; - } - if (!sA[1].Contains("E") && !sA[1].Contains("e")) - { - if (!sA[1].Contains("W") && !sA[1].Contains("w")) - { - return false;//No Direction Found - } - lngR = -1; - } - - sA[0] = Regex.Replace(sA[0], "[^0-9.]", ""); - sA[1] = Regex.Replace(sA[1], "[^0-9.]", ""); - - if (!double.TryParse(sA[0], out lat)) - { return false; } - if (!double.TryParse(sA[1], out lng)) - { return false; } - lat *= latR; - lng *= lngR; - d = new double[] { lat, lng }; - return true; - } - - return false; - } - private static bool TryDegreeDecimalMinute(string s, out double[] d) - { - d = null; - if (Regex.Matches(s, @"[a-zA-Z]").Count != 2) { return false; } //Should only contain 1 letter. - - string[] sA = SpecialSplit(s,true); - if (sA.Count() == 4 || sA.Count() == 6) - { - double latD; - double latMS; - double lngD; - double lngMS; - - double latR = 0; //Sets 1 if South - double lngR = 0; //Sets 1 if West - - //Contact get in order to combine directional indicator together with string - //Should reduce 6 items to 4 - if (sA.Count() == 6) - { - if (char.IsLetter(sA[0][0])) { sA[0] += sA[1]; sA[1] = sA[2]; } - else if (char.IsLetter(sA[1][0])) { sA[0] += sA[1]; sA[1] = sA[2]; } - else if (char.IsLetter(sA[2][0])) { sA[0] += sA[2]; } - else { return false; } - - if (char.IsLetter(sA[3][0])) { sA[3] += sA[4]; sA[4] = sA[5]; } - else if (char.IsLetter(sA[4][0])) { sA[3] += sA[4]; sA[4] = sA[5]; } - else if (char.IsLetter(sA[5][0])) { sA[3] += sA[5]; } - else { return false; } - - //Shift values for below logic - sA[2] = sA[3]; - sA[3] = sA[4]; - } - - //Find Directions - if (!sA[0].Contains("N") && !sA[0].Contains("n") && !sA[1].Contains("N") && !sA[1].Contains("n")) - { - if (!sA[0].Contains("S") && !sA[0].Contains("s") && !sA[1].Contains("S") && !sA[1].Contains("s")) - { - return false;//No Direction Found - } - latR = 1; - } - if (!sA[2].Contains("E") && !sA[2].Contains("e") && !sA[3].Contains("E") && !sA[3].Contains("e")) - { - if (!sA[2].Contains("W") && !sA[2].Contains("w") && !sA[3].Contains("W") && !sA[3].Contains("w")) - { - return false;//No Direction Found - } - lngR = 1; - } - - sA[0] = Regex.Replace(sA[0], "[^0-9.]", ""); - sA[1] = Regex.Replace(sA[1], "[^0-9.]", ""); - sA[2] = Regex.Replace(sA[2], "[^0-9.]", ""); - sA[3] = Regex.Replace(sA[3], "[^0-9.]", ""); - - if (!double.TryParse(sA[0], out latD)) - { return false; } - if (!double.TryParse(sA[1], out latMS)) - { return false; } - if (!double.TryParse(sA[2], out lngD)) - { return false; } - if (!double.TryParse(sA[3], out lngMS)) - { return false; } - - d = new double[] { latD, latMS, latR, lngD, lngMS, lngR }; - return true; - } - return false; - } - private static bool TryDegreeMinuteSecond(string s, out double[] d) - { - d = null; - if (Regex.Matches(s, @"[a-zA-Z]").Count != 2) { return false; } //Should only contain 1 letter. - - string[] sA = SpecialSplit(s,true); - if (sA.Count() == 6 || sA.Count() == 8) - { - double latD; - double latM; - double latS; - double lngD; - double lngM; - double lngS; - - double latR = 0; //Sets 1 if South - double lngR = 0; //Sets 1 if West - - //Contact get in order to combine directional indicator together with string - //Should reduce 8 items to 6 - if (sA.Count() == 8) - { - if (char.IsLetter(sA[0][0])) { sA[0] += sA[1]; sA[1] = sA[2]; sA[2] = sA[3]; } - else if (char.IsLetter(sA[1][0])) { sA[0] += sA[1]; sA[1] = sA[2]; sA[2] = sA[3]; } - else if (char.IsLetter(sA[3][0])) { sA[0] += sA[3]; } - else { return false; } - - if (char.IsLetter(sA[4][0])) { sA[4] += sA[5]; sA[5] = sA[6]; sA[6] = sA[7]; } - else if (char.IsLetter(sA[5][0])) { sA[4] += sA[5]; sA[5] = sA[6]; sA[6] = sA[7]; } - else if (char.IsLetter(sA[7][0])) { sA[4] += sA[7]; } - else { return false; } - - //Shift values for below logic - sA[3] = sA[4]; - sA[4] = sA[5]; - sA[5] = sA[6]; - } - - //Find Directions - if (!sA[0].Contains("N") && !sA[0].Contains("n") && !sA[2].Contains("N") && !sA[2].Contains("n")) - { - if (!sA[0].Contains("S") && !sA[0].Contains("s") && !sA[2].Contains("S") && !sA[2].Contains("s")) - { - return false;//No Direction Found - } - latR = 1; - } - if (!sA[3].Contains("E") && !sA[3].Contains("e") && !sA[5].Contains("E") && !sA[5].Contains("e")) - { - if (!sA[3].Contains("W") && !sA[3].Contains("w") && !sA[5].Contains("W") && !sA[5].Contains("w")) - { - return false;//No Direction Found - } - lngR = 1; - } - sA[0] = Regex.Replace(sA[0], "[^0-9.]", ""); - sA[1] = Regex.Replace(sA[1], "[^0-9.]", ""); - sA[2] = Regex.Replace(sA[2], "[^0-9.]", ""); - sA[3] = Regex.Replace(sA[3], "[^0-9.]", ""); - sA[4] = Regex.Replace(sA[4], "[^0-9.]", ""); - sA[5] = Regex.Replace(sA[5], "[^0-9.]", ""); - - if (!double.TryParse(sA[0], out latD)) - { return false; } - if (!double.TryParse(sA[1], out latM)) - { return false; } - if (!double.TryParse(sA[2], out latS)) - { return false; } - if (!double.TryParse(sA[3], out lngD)) - { return false; } - if (!double.TryParse(sA[4], out lngM)) - { return false; } - if (!double.TryParse(sA[5], out lngS)) - { return false; } - - d = new double[] { latD, latM, latS, latR, lngD, lngM, lngS, lngR }; - return true; - } - return false; - } - private static bool TryUTM(string s, out string[] utm) - { - utm = null; - string[] sA = SpecialSplit(s,false); - if (sA.Count() == 3 || sA.Count() == 4) - { - double zone; - string zoneL; - double easting; - double northing; - - if (sA.Count() == 4) - { - - if (char.IsLetter(sA[0][0])) { sA[0] += sA[1]; sA[1] = sA[2]; sA[2] = sA[3]; } - else if (char.IsLetter(sA[1][0])) { sA[0] += sA[1]; sA[1] = sA[2]; sA[2] = sA[3]; } - else { return false; } - } - zoneL = new string(sA[0].Where(Char.IsLetter).ToArray()); - if (zoneL == string.Empty) { return false; } - sA[0] = Regex.Replace(sA[0], "[^0-9.]", ""); - - if (!double.TryParse(sA[0], out zone)) - { return false; } - if (!double.TryParse(sA[1], out easting)) - { return false; } - if (!double.TryParse(sA[2], out northing)) - { return false; } - - utm = new string[] { zone.ToString(), zoneL, easting.ToString(), northing.ToString() }; - return true; - } - return false; - } - private static bool TryMGRS(string s, out string[] mgrs) - { - mgrs = null; - string[] sA = SpecialSplit(s,false); - if (sA.Count() == 4 || sA.Count() == 5) - { - double zone; - string zoneL; - string diagraph; - double easting; - double northing; - - if (sA.Count() == 5) - { - if (char.IsLetter(sA[0][0])) { sA[0] += sA[1]; sA[1] = sA[2]; sA[2] = sA[3]; } - else if (char.IsLetter(sA[1][0])) { sA[0] += sA[1]; sA[1] = sA[2]; sA[2] = sA[3]; } - else { return false; } - } - zoneL = new string(sA[0].Where(Char.IsLetter).ToArray()); - if (zoneL == string.Empty) { return false; } - sA[0] = Regex.Replace(sA[0], "[^0-9.]", ""); - diagraph = sA[1]; - if (!double.TryParse(sA[0], out zone)) - { return false; } - if (!double.TryParse(sA[2], out easting)) - { return false; } - if (!double.TryParse(sA[3], out northing)) - { return false; } - - mgrs = new string[] { zone.ToString(), zoneL, diagraph, easting.ToString(), northing.ToString() }; - return true; - } - return false; - } - private static bool TryCartesian(string s, out double[] d) - { - d = null; - string[] sA = SpecialSplit(s,false); - - if (sA.Count() == 3) - { - double x; - double y; - double z; - if (!double.TryParse(sA[0], out x)) - { return false; } - if (!double.TryParse(sA[1], out y)) - { return false; } - if (!double.TryParse(sA[2], out z)) - { return false; } - d = new double[] { x, y, z }; - return true; - } - return false; - } - - //KEEP DASHES FOR SIGNED AND CARTESIAN AS THEY ARE USED FOR NEGATVE VALUES - private static string[] SpecialSplit(string s, bool removeDashes) - { - s = s.Replace("°", " "); - s = s.Replace("º", " "); - s = s.Replace("'", " "); - s = s.Replace("\"", " "); - s = s.Replace(",", " "); - s = s.Replace("mE", " "); - s = s.Replace("mN", " "); - if (removeDashes) - { - s = s.Replace("-", " "); - } - return s.Split(new char[0], StringSplitOptions.RemoveEmptyEntries); - } + //KEEP DASHES FOR SIGNED AND CARTESIAN AS THEY ARE USED FOR NEGATVE VALUES + private static String[] SpecialSplit(String s, Boolean removeDashes) { + s = s.Replace("°", " "); + s = s.Replace("º", " "); + s = s.Replace("'", " "); + s = s.Replace("\"", " "); + s = s.Replace(",", " "); + s = s.Replace("mE", " "); + s = s.Replace("mN", " "); + if (removeDashes) { + s = s.Replace("-", " "); + } + return s.Split(new Char[0], StringSplitOptions.RemoveEmptyEntries); } - internal class FormatFinder_CoordPart - { - //Add main to Coordinate and tunnel to Format class. Add private methods to format. - //WHEN PARSING NO EXCPETIONS FOR OUT OF RANGE ARGS WILL BE THROWN - public static bool TryParse(string coordString, out CoordinatePart cp) - { - //Turn of eagerload for efficiency - EagerLoad eg = new EagerLoad(); - int type = 0; //0 = unspecifed, 1 = lat, 2 = long; - eg.Cartesian = false; - eg.Celestial = false; - eg.UTM_MGRS = false; - cp = null; - Coordinate c = new Coordinate(eg); - string s = coordString; - s = s.Trim(); //Trim all spaces before and after string - double[] d; + } + internal class FormatFinder_CoordPart { + //Add main to Coordinate and tunnel to Format class. Add private methods to format. + //WHEN PARSING NO EXCPETIONS FOR OUT OF RANGE ARGS WILL BE THROWN + public static Boolean TryParse(String coordString, out CoordinatePart cp) { + //Turn of eagerload for efficiency + EagerLoad eg = new EagerLoad(); + Int32 type = 0; //0 = unspecifed, 1 = lat, 2 = long; + eg.Cartesian = false; + eg.Celestial = false; + eg.UTM_MGRS = false; + cp = null; + Coordinate c = new Coordinate(eg); + String s = coordString; + s = s.Trim(); //Trim all spaces before and after string - if (s[0] == ',') - { - type = 2; - s = s.Replace(",", ""); - s = s.Trim(); - } - if (s[0] == '*') - { - type = 1; - s = s.Replace("*", ""); - s = s.Trim(); - } + if (s[0] == ',') { + type = 2; + s = s.Replace(",", ""); + s = s.Trim(); + } + if (s[0] == '*') { + type = 1; + s = s.Replace("*", ""); + s = s.Trim(); + } - if (TrySignedDegree(s, type, out d)) - { - try - { - switch (type) - { - case 0: - //Attempt Lat first (default for signed) - try - { - cp = new CoordinatePart(d[0], CoordinateType.Lat); - c.Parse_Format = Parse_Format_Type.Signed_Degree; - return true; - } - catch - { - cp = new CoordinatePart(d[0], CoordinateType.Long); - c.Parse_Format = Parse_Format_Type.Signed_Degree; - return true; - } - case 1: - //Attempt Lat - cp = new CoordinatePart(d[0], CoordinateType.Lat); - c.Parse_Format = Parse_Format_Type.Signed_Degree; - return true; - case 2: - //Attempt long - cp = new CoordinatePart(d[0], CoordinateType.Long); - c.Parse_Format = Parse_Format_Type.Signed_Degree; - return true; - } - } - catch - { - //silent fail - } - } - //SIGNED DEGREE FAILED, REMOVE DASHES FOR OTHER FORMATS - s = s.Replace("-", " "); - - //All other formats should contain 1 letter. - if (Regex.Matches(s, @"[a-zA-Z]").Count != 1) { return false; } //Should only contain 1 letter. - //Get Coord Direction - int direction = Find_Position(s); - - if (direction == -1) - { - return false; //No direction found - } - //If Coordinate type int specified, look for mismatch - if (type == 1 && (direction == 1 || direction == 3)) - { - return false; //mismatch - } - if (type == 2 && (direction == 0 || direction == 2)) - { - return false; //mismatch - } - CoordinateType t; - if (direction == 0 || direction == 2) { t = CoordinateType.Lat; } - else { t = CoordinateType.Long; } - - s = Regex.Replace(s, "[^0-9. ]", ""); //Remove directional character - s = s.Trim(); //Trim all spaces before and after string - - //Try Decimal Degree with Direction - if (TryDecimalDegree(s, direction, out d)) - { - try - { - cp = new CoordinatePart(d[0], t); - c.Parse_Format = Parse_Format_Type.Decimal_Degree; - return true; - } - catch - {//Parser failed try next method - } - } - //Try DDM - if (TryDegreeDecimalMinute(s, out d)) - { - try - { - //0 Degree - //1 Minute - //2 Direction (0 = N, 1 = E, 2 = S, 3 = W) - cp = new CoordinatePart((int)d[0], d[1], (CoordinatesPosition)direction); - c.Parse_Format = Parse_Format_Type.Degree_Decimal_Minute; - return true; - } - catch - { - //Parser failed try next method - } - } - //Try DMS - if (TryDegreeMinuteSecond(s, out d)) - { - try - { - //0 Degree - //1 Minute - //2 Second - //3 Direction (0 = N, 1 = E, 2 = S, 3 = W) - cp = new CoordinatePart((int)d[0], (int)d[1], d[2], (CoordinatesPosition)direction); - c.Parse_Format = Parse_Format_Type.Degree_Minute_Second; - return true; - } - catch - {//Parser failed try next method - } - } - - return false; - } - - private static bool TrySignedDegree(string s, int t, out double[] d) - { - d = null; - if (Regex.Matches(s, @"[a-zA-Z]").Count != 0) { return false; } //Should contain no letters - - string[] sA = SpecialSplit(s, false); - double deg; - double min; //Minutes & MinSeconds - double sec; - - int sign = 1; - switch (sA.Count()) - { - case 1: - if (!double.TryParse(sA[0], out deg)) - { return false; } - d = new double[] { deg }; - return true; - case 2: - if (!double.TryParse(sA[0], out deg)) - { return false; } - if (!double.TryParse(sA[1], out min)) - { return false; } - - if (deg < 0) { sign = -1; } - if (min >= 60 || min < 0) { return false; } //Handle in parser as degree will be incorrect. - d = new double[] { (Math.Abs(deg) + (min / 60.0)) * sign }; - return true; - case 3: - if (!double.TryParse(sA[0], out deg)) - { return false; } - if (!double.TryParse(sA[1], out min)) - { return false; } - if (!double.TryParse(sA[2], out sec)) - { return false; } - if (min >= 60 || min < 0) { return false; } //Handle in parser as degree will be incorrect. - if (sec >= 60 || sec < 0) { return false; } //Handle in parser as degree will be incorrect. - - if (deg < 0) { sign = -1; } - d = new double[] { (Math.Abs(deg) + (min / 60.0) + (sec / 3600.0)) * sign }; - return true; - default: - return false; - } - } - private static bool TryDecimalDegree(string s, int direction, out double[] d) - { - d = null; - int sign = 1; - //S or W - if (direction == 2 || direction == 3) - { - sign = -1; - } - double coord; - - string[] sA = SpecialSplit(s, true); - - if (sA.Count() == 1) - { - if (!double.TryParse(s, out coord)) - { return false; } - - coord *= sign; - d = new double[] { coord }; + if (TrySignedDegree(s, type, out Double[] d)) { + try { + switch (type) { + case 0: + //Attempt Lat first (default for signed) + try { + cp = new CoordinatePart(d[0], CoordinateType.Lat); + c.Parse_Format = Parse_Format_Type.Signed_Degree; return true; - } - - return false; - } - private static bool TryDegreeDecimalMinute(string s, out double[] d) - { - d = null; - - double deg; - double minSec; - - - string[] sA = SpecialSplit(s,true); - if (sA.Count() == 2) - { - if (!double.TryParse(sA[0], out deg)) - { return false; } - if (!double.TryParse(sA[1], out minSec)) - { return false; } - - d = new double[] { deg, minSec }; + } catch { + cp = new CoordinatePart(d[0], CoordinateType.Long); + c.Parse_Format = Parse_Format_Type.Signed_Degree; return true; - } - return false; + } + case 1: + //Attempt Lat + cp = new CoordinatePart(d[0], CoordinateType.Lat); + c.Parse_Format = Parse_Format_Type.Signed_Degree; + return true; + case 2: + //Attempt long + cp = new CoordinatePart(d[0], CoordinateType.Long); + c.Parse_Format = Parse_Format_Type.Signed_Degree; + return true; + } + } catch { + //silent fail } - private static bool TryDegreeMinuteSecond(string s, out double[] d) - { - d = null; + } + //SIGNED DEGREE FAILED, REMOVE DASHES FOR OTHER FORMATS + s = s.Replace("-", " "); + //All other formats should contain 1 letter. + if (Regex.Matches(s, @"[a-zA-Z]").Count != 1) { return false; } //Should only contain 1 letter. + //Get Coord Direction + Int32 direction = Find_Position(s); - double deg; - double min; - double sec; + if (direction == -1) { + return false; //No direction found + } + //If Coordinate type int specified, look for mismatch + if (type == 1 && (direction == 1 || direction == 3)) { + return false; //mismatch + } + if (type == 2 && (direction == 0 || direction == 2)) { + return false; //mismatch + } + CoordinateType t = direction == 0 || direction == 2 ? CoordinateType.Lat : CoordinateType.Long; + s = Regex.Replace(s, "[^0-9. ]", ""); //Remove directional character + s = s.Trim(); //Trim all spaces before and after string - string[] sA = SpecialSplit(s,true); - if (sA.Count() == 3) - { - - if (!double.TryParse(sA[0], out deg)) - { return false; } - if (!double.TryParse(sA[1], out min)) - { return false; } - if (!double.TryParse(sA[2], out sec)) - { return false; } - - d = new double[] { deg, min, sec }; - return true; - } - return false; + //Try Decimal Degree with Direction + if (TryDecimalDegree(s, direction, out d)) { + try { + cp = new CoordinatePart(d[0], t); + c.Parse_Format = Parse_Format_Type.Decimal_Degree; + return true; + } catch {//Parser failed try next method } - - private static int Find_Position(string s) - { - //N=0 - //E=1 - //S=2 - //W=3 - //NOPOS = -1 - //Find Directions - - int part = -1; - if (s.Contains("N") || s.Contains("n")) - { - part = 0; - } - if (s.Contains("E") || s.Contains("e")) - { - part = 1; - } - if (s.Contains("S") || s.Contains("s")) - { - part = 2; - - } - if (s.Contains("W") || s.Contains("w")) - { - part = 3; - } - return part; + } + //Try DDM + if (TryDegreeDecimalMinute(s, out d)) { + try { + //0 Degree + //1 Minute + //2 Direction (0 = N, 1 = E, 2 = S, 3 = W) + cp = new CoordinatePart((Int32)d[0], d[1], (CoordinatesPosition)direction); + c.Parse_Format = Parse_Format_Type.Degree_Decimal_Minute; + return true; + } catch { + //Parser failed try next method } - - //KEEP DASHES FOR SIGNED AND CARTESIAN AS THEY ARE USED FOR NEGATVE VALUES - private static string[] SpecialSplit(string s, bool removeDashes) - { - s = s.Replace("°", " "); - s = s.Replace("º", " "); - s = s.Replace("'", " "); - s = s.Replace("\"", " "); - s = s.Replace(",", " "); - s = s.Replace("mE", " "); - s = s.Replace("mN", " "); - if(removeDashes) - { - s = s.Replace("-", " "); - } - return s.Split(new char[0], StringSplitOptions.RemoveEmptyEntries); + } + //Try DMS + if (TryDegreeMinuteSecond(s, out d)) { + try { + //0 Degree + //1 Minute + //2 Second + //3 Direction (0 = N, 1 = E, 2 = S, 3 = W) + cp = new CoordinatePart((Int32)d[0], (Int32)d[1], d[2], (CoordinatesPosition)direction); + c.Parse_Format = Parse_Format_Type.Degree_Minute_Second; + return true; + } catch {//Parser failed try next method } + } + + return false; } + + private static Boolean TrySignedDegree(String s, Int32 _, out Double[] d) { + d = null; + if (Regex.Matches(s, @"[a-zA-Z]").Count != 0) { return false; } //Should contain no letters + + String[] sA = SpecialSplit(s, false); + Double deg; + Double min; //Minutes & MinSeconds + Double sec; + + Int32 sign = 1; + switch (sA.Count()) { + case 1: + if (!Double.TryParse(sA[0], out deg)) { return false; } + d = new Double[] { deg }; + return true; + case 2: + if (!Double.TryParse(sA[0], out deg)) { return false; } + if (!Double.TryParse(sA[1], out min)) { return false; } + + if (deg < 0) { sign = -1; } + if (min >= 60 || min < 0) { return false; } //Handle in parser as degree will be incorrect. + d = new Double[] { (Math.Abs(deg) + min / 60.0) * sign }; + return true; + case 3: + if (!Double.TryParse(sA[0], out deg)) { return false; } + if (!Double.TryParse(sA[1], out min)) { return false; } + if (!Double.TryParse(sA[2], out sec)) { return false; } + if (min >= 60 || min < 0) { return false; } //Handle in parser as degree will be incorrect. + if (sec >= 60 || sec < 0) { return false; } //Handle in parser as degree will be incorrect. + + if (deg < 0) { sign = -1; } + d = new Double[] { (Math.Abs(deg) + min / 60.0 + sec / 3600.0) * sign }; + return true; + default: + return false; + } + } + private static Boolean TryDecimalDegree(String s, Int32 direction, out Double[] d) { + d = null; + Int32 sign = 1; + //S or W + if (direction == 2 || direction == 3) { + sign = -1; + } + + String[] sA = SpecialSplit(s, true); + + if (sA.Count() == 1) { + if (!Double.TryParse(s, out Double coord)) { return false; } + + coord *= sign; + d = new Double[] { coord }; + return true; + } + + return false; + } + private static Boolean TryDegreeDecimalMinute(String s, out Double[] d) { + d = null; + + + + String[] sA = SpecialSplit(s, true); + if (sA.Count() == 2) { + if (!Double.TryParse(sA[0], out Double deg)) { return false; } + if (!Double.TryParse(sA[1], out Double minSec)) { return false; } + + d = new Double[] { deg, minSec }; + return true; + } + return false; + } + private static Boolean TryDegreeMinuteSecond(String s, out Double[] d) { + d = null; + + + + String[] sA = SpecialSplit(s, true); + if (sA.Count() == 3) { + + if (!Double.TryParse(sA[0], out Double deg)) { return false; } + if (!Double.TryParse(sA[1], out Double min)) { return false; } + if (!Double.TryParse(sA[2], out Double sec)) { return false; } + + d = new Double[] { deg, min, sec }; + return true; + } + return false; + } + + private static Int32 Find_Position(String s) { + //N=0 + //E=1 + //S=2 + //W=3 + //NOPOS = -1 + //Find Directions + + Int32 part = -1; + if (s.Contains("N") || s.Contains("n")) { + part = 0; + } + if (s.Contains("E") || s.Contains("e")) { + part = 1; + } + if (s.Contains("S") || s.Contains("s")) { + part = 2; + + } + if (s.Contains("W") || s.Contains("w")) { + part = 3; + } + return part; + } + + //KEEP DASHES FOR SIGNED AND CARTESIAN AS THEY ARE USED FOR NEGATVE VALUES + private static String[] SpecialSplit(String s, Boolean removeDashes) { + s = s.Replace("°", " "); + s = s.Replace("º", " "); + s = s.Replace("'", " "); + s = s.Replace("\"", " "); + s = s.Replace(",", " "); + s = s.Replace("mE", " "); + s = s.Replace("mN", " "); + if (removeDashes) { + s = s.Replace("-", " "); + } + return s.Split(new Char[0], StringSplitOptions.RemoveEmptyEntries); + } + } } diff --git a/CoordinateSharp/Coordinate.UTM.cs b/CoordinateSharp/Coordinate.UTM.cs index 1add280..1e1dab3 100644 --- a/CoordinateSharp/Coordinate.UTM.cs +++ b/CoordinateSharp/Coordinate.UTM.cs @@ -1,592 +1,491 @@ using System; -using System.Linq; -using System.Diagnostics; using System.ComponentModel; +using System.Diagnostics; +using System.Linq; -namespace CoordinateSharp -{ +namespace CoordinateSharp { + /// + /// Universal Transverse Mercator (UTM) coordinate system. Uses the WGS 84 Datum. + /// + [Serializable] + public class UniversalTransverseMercator : INotifyPropertyChanged { /// - /// Universal Transverse Mercator (UTM) coordinate system. Uses the WGS 84 Datum. + /// Creates a UniversalTransverMercator object with a WGS84 Datum. /// - [Serializable] - public class UniversalTransverseMercator : INotifyPropertyChanged - { - /// - /// Creates a UniversalTransverMercator object with a WGS84 Datum. - /// - /// Latitude zone - /// Longitude zone - /// Easting - /// Northing - public UniversalTransverseMercator(string latz, int longz, double est, double nrt) - { - if (longz < 1 || longz > 60) { Debug.WriteLine("Longitudinal zone out of range", "UTM longitudinal zones must be between 1-60."); } - if (!Verify_Lat_Zone(latz)) { Debug.WriteLine("Latitudinal zone invalid", "UTM latitudinal zone was unrecognized."); } - if (est < 160000 || est > 834000) { Debug.WriteLine("The Easting value provided is outside the max allowable range. Use with caution."); } - if (nrt < 0 || nrt > 10000000) { Debug.WriteLine("Northing out of range", "Northing must be between 0-10,000,000."); } + /// Latitude zone + /// Longitude zone + /// Easting + /// Northing + public UniversalTransverseMercator(String latz, Int32 longz, Double est, Double nrt) { + if (longz < 1 || longz > 60) { Debug.WriteLine("Longitudinal zone out of range", "UTM longitudinal zones must be between 1-60."); } + if (!this.Verify_Lat_Zone(latz)) { Debug.WriteLine("Latitudinal zone invalid", "UTM latitudinal zone was unrecognized."); } + if (est < 160000 || est > 834000) { Debug.WriteLine("The Easting value provided is outside the max allowable range. Use with caution."); } + if (nrt < 0 || nrt > 10000000) { Debug.WriteLine("Northing out of range", "Northing must be between 0-10,000,000."); } - latZone = latz; - longZone =longz; - easting = est; - northing = nrt; + this.latZone = latz; + this.longZone = longz; + this.easting = est; + this.northing = nrt; - equatorial_radius = 6378137.0; - inverse_flattening = 298.257223563; + this.equatorial_radius = 6378137.0; + this.inverse_flattening = 298.257223563; + } + /// + /// Creates a UniversalTransverMercator object with a custom Datum. + /// + /// Latitude zone + /// Longitude zone + /// Easting + /// Northing + /// Equatorial Radius + /// Inverse Flattening + public UniversalTransverseMercator(String latz, Int32 longz, Double est, Double nrt, Double radius, Double flaten) { + if (longz < 1 || longz > 60) { Debug.WriteLine("Longitudinal zone out of range", "UTM longitudinal zones must be between 1-60."); } + if (!this.Verify_Lat_Zone(latz)) { Debug.WriteLine("Latitudinal zone invalid", "UTM latitudinal zone was unrecognized."); } + if (est < 160000 || est > 834000) { Debug.WriteLine("The Easting value provided is outside the max allowable range. Use with caution."); } + if (nrt < 0 || nrt > 10000000) { Debug.WriteLine("Northing out of range", "Northing must be between 0-10,000,000."); } + + this.latZone = latz; + this.longZone = longz; + this.easting = est; + this.northing = nrt; + + this.equatorial_radius = radius; + this.inverse_flattening = flaten; + } + + //private readonly Coordinate coordinate; + + internal Double equatorial_radius; + internal Double inverse_flattening; + private String latZone; + private Int32 longZone; + + private Double easting; + private Double northing; + + /// + /// UTM Zone Letter + /// + public String LatZone { + get => this.latZone; + set { + if (this.latZone != value) { + this.latZone = value; } - /// - /// Creates a UniversalTransverMercator object with a custom Datum. - /// - /// Latitude zone - /// Longitude zone - /// Easting - /// Northing - /// Equatorial Radius - /// Inverse Flattening - public UniversalTransverseMercator(string latz, int longz, double est, double nrt, double radius, double flaten) - { - if (longz < 1 || longz > 60) { Debug.WriteLine("Longitudinal zone out of range", "UTM longitudinal zones must be between 1-60."); } - if (!Verify_Lat_Zone(latz)) { Debug.WriteLine("Latitudinal zone invalid", "UTM latitudinal zone was unrecognized."); } - if (est < 160000 || est > 834000) { Debug.WriteLine("The Easting value provided is outside the max allowable range. Use with caution."); } - if (nrt < 0 || nrt > 10000000) { Debug.WriteLine("Northing out of range", "Northing must be between 0-10,000,000."); } - - latZone = latz; - longZone = longz; - easting = est; - northing = nrt; - - equatorial_radius = radius; - inverse_flattening = flaten; + } + } + /// + /// UTM Zone Number + /// + public Int32 LongZone { + get => this.longZone; + set { + if (this.longZone != value) { + this.longZone = value; } - - private Coordinate coordinate; - - internal double equatorial_radius; - internal double inverse_flattening; - private string latZone; - private int longZone; - - private double easting; - private double northing; - - private bool withinCoordinateSystemBounds = true; - - /// - /// UTM Zone Letter - /// - public string LatZone - { - get { return latZone; } - set - { - if (latZone != value) - { - latZone = value; - } - } + } + } + /// + /// UTM Easting + /// + public Double Easting { + get => this.easting; + set { + if (this.easting != value) { + this.easting = value; } - /// - /// UTM Zone Number - /// - public int LongZone - { - get { return longZone; } - set - { - if (longZone != value) - { - longZone = value; - } - } + } + } + /// + /// UTM Northing + /// + public Double Northing { + get => this.northing; + set { + if (this.northing != value) { + this.northing = value; } - /// - /// UTM Easting - /// - public double Easting - { - get { return easting; } - set - { - if (easting != value) - { - easting = value; - } - } - } - /// - /// UTM Northing - /// - public double Northing - { - get { return northing; } - set - { - if (northing != value) - { - northing = value; - } - } - } - - /// - /// Datum Equatorial Radius / Semi Major Axis - /// - public double Equatorial_Radius - { - get { return equatorial_radius; } - } - /// - /// Datum Flattening - /// - public double Inverse_Flattening - { - get { return inverse_flattening; } - } - - /// - /// Is the UTM conversion within the coordinate system's accurate boundaries after conversion from Lat/Long. - /// - public bool WithinCoordinateSystemBounds - { - get { return withinCoordinateSystemBounds; } - } - - /// - /// Constructs a UTM object based off DD Lat/Long - /// - /// DD Latitude - /// DD Longitide - /// Parent Coordinate Object - internal UniversalTransverseMercator(double lat, double longi, Coordinate c) - { - //validate coords - - //if (lat > 180) { throw new ArgumentOutOfRangeException("Degrees out of range", "Longitudinal coordinate decimal cannot be greater than 180."); } - //if (lat < -180) { throw new ArgumentOutOfRangeException("Degrees out of range", "Longitudinal coordinate decimal cannot be less than 180."); } - - //if (longi > 90) { throw new ArgumentOutOfRangeException("Degrees out of range", "Latitudinal coordinate decimal cannot be greater than 90."); } - //if (longi < -90) { throw new ArgumentOutOfRangeException("Degrees out of range", "Latitudinal coordinate decimal cannot be less than 90."); } - equatorial_radius = 6378137.0; - inverse_flattening = 298.257223563; - ToUTM(lat, longi, this); - - coordinate = c; - } - /// - /// Constructs a UTM object based off DD Lat/Long - /// - /// DD Latitude - /// DD Longitide - /// Parent Coordinate Object - /// Equatorial Radius - /// Flattening - internal UniversalTransverseMercator(double lat, double longi, Coordinate c,double rad,double flt) - { - equatorial_radius = rad; - inverse_flattening = flt; - ToUTM(lat, longi, this); - - coordinate = c; - } - /// - /// Constructs a UTM object based off a UTM coordinate - /// Not yet implemented - /// - /// Zone Letter - /// Zone Number - /// Easting - /// Northing - /// Parent Coordinate Object - /// Equatorial Radius - /// Inverse Flattening - internal UniversalTransverseMercator(string latz, int longz, double e, double n, Coordinate c, double rad, double flt) - { - //validate utm - if (longz < 1 || longz > 60) { Debug.WriteLine("Longitudinal zone out of range", "UTM longitudinal zones must be between 1-60."); } - if (!Verify_Lat_Zone(latz)) { throw new ArgumentException("Latitudinal zone invalid", "UTM latitudinal zone was unrecognized."); } - if (e < 160000 || e > 834000) { Debug.WriteLine("The Easting value provided is outside the max allowable range. If this is intentional, use with caution."); } - if (n < 0 || n > 10000000) { throw new ArgumentOutOfRangeException("Northing out of range", "Northing must be between 0-10,000,000."); } - equatorial_radius = rad; - inverse_flattening = flt; - latZone = latz; - longZone = longz; - - easting = e; - northing = n; - - coordinate = c; - if (c.Latitude.DecimalDegree <= -80 || c.Latitude.DecimalDegree >= 84) { withinCoordinateSystemBounds = false; } - else { withinCoordinateSystemBounds = true; } - } - - /// - /// Verifies Lat zone when convert from UTM to DD Lat/Long - /// - /// Zone Letter - /// boolean - private bool Verify_Lat_Zone(string l) - { - if (LatZones.longZongLetters.Where(x => x == l.ToUpper()).Count() != 1) - { - return false; - } - return true; - } - private double degreeToRadian(double degree) - { - return degree * Math.PI / 180; - } - /// - /// Assigns UTM values based of Lat/Long - /// - /// DD Latitude - /// DD longitude - /// UTM Object to modify - internal void ToUTM(double lat, double longi, UniversalTransverseMercator utm) - { - string letter = ""; - double easting = 0; - double northing = 0; - int zone = (int)Math.Floor(longi / 6 + 31); - if (lat < -72) - letter = "C"; - else if (lat < -64) - letter = "D"; - else if (lat < -56) - letter = "E"; - else if (lat < -48) - letter = "F"; - else if (lat < -40) - letter = "G"; - else if (lat < -32) - letter = "H"; - else if (lat < -24) - letter = "J"; - else if (lat < -16) - letter = "K"; - else if (lat < -8) - letter = "L"; - else if (lat < 0) - letter = "M"; - else if (lat < 8) - letter = "N"; - else if (lat < 16) - letter = "P"; - else if (lat < 24) - letter = "Q"; - else if (lat < 32) - letter = "R"; - else if (lat < 40) - letter = "S"; - else if (lat < 48) - letter = "T"; - else if (lat < 56) - letter = "U"; - else if (lat < 64) - letter = "V"; - else if (lat < 72) - letter = "W"; - else - letter = "X"; - - double a = utm.equatorial_radius; - double f = 1.0 / utm.inverse_flattening; - double b = a * (1 - f); // polar radius - - double e = Math.Sqrt(1 - Math.Pow(b, 2) / Math.Pow(a, 2)); - double e0 = e / Math.Sqrt(1 - Math.Pow(e, 1)); - - double drad = Math.PI / 180; - double k0 = 0.9996; - - double phi = lat * drad; // convert latitude to radians - double lng = longi * drad; // convert longitude to radians - double utmz = 1 + Math.Floor((longi + 180) / 6.0); // longitude to utm zone - double zcm = 3 + 6.0 * (utmz - 1) - 180; // central meridian of a zone - // this gives us zone A-B for below 80S - double esq = (1 - (b / a) * (b / a)); - double e0sq = e * e / (1 - Math.Pow(e, 2)); - double M = 0; - - double N = a / Math.Sqrt(1 - Math.Pow(e * Math.Sin(phi), 2)); - double T = Math.Pow(Math.Tan(phi), 2); - double C = e0sq * Math.Pow(Math.Cos(phi), 2); - double A = (longi - zcm) * drad * Math.Cos(phi); - - // calculate M (USGS style) - M = phi * (1 - esq * (1.0 / 4.0 + esq * (3.0 / 64.0 + 5.0 * esq / 256.0))); - M = M - Math.Sin(2.0 * phi) * (esq * (3.0 / 8.0 + esq * (3.0 / 32.0 + 45.0 * esq / 1024.0))); - M = M + Math.Sin(4.0 * phi) * (esq * esq * (15.0 / 256.0 + esq * 45.0 / 1024.0)); - M = M - Math.Sin(6.0 * phi) * (esq * esq * esq * (35.0 / 3072.0)); - M = M * a;//Arc length along standard meridian - - double M0 = 0;// if another point of origin is used than the equator - - // Calculate the UTM values... - // first the easting - var x = k0 * N * A * (1 + A * A * ((1 - T + C) / 6 + A * A * (5 - 18 * T + T * T + 72.0 * C - 58 * e0sq) / 120.0)); //Easting relative to CM - x = x + 500000; // standard easting - - // Northing - - double y = k0 * (M - M0 + N * Math.Tan(phi) * (A * A * (1 / 2.0 + A * A * ((5 - T + 9 * C + 4 * C * C) / 24.0 + A * A * (61 - 58 * T + T * T + 600 * C - 330 * e0sq) / 720.0)))); // first from the equator - double yg = y + 10000000; //yg = y global, from S. Pole - if (y < 0) - { - y = 10000000 + y; // add in false northing if south of the equator - } - - - easting = Math.Round(10 * (x)) / 10.0; - northing = Math.Round(10 * y) / 10.0; - - utm.latZone = letter; - utm.longZone = zone; - utm.easting = easting; - utm.northing = northing; - - if(lat<=-80 || lat >= 84) { withinCoordinateSystemBounds = false; } - else { withinCoordinateSystemBounds = true; } - } - - /// - /// UTM Default String Format - /// - /// UTM Formatted Coordinate String - public override string ToString() - { - if (!withinCoordinateSystemBounds) { return ""; }//MGRS Coordinate is outside its reliable boundaries. Return empty. - return longZone.ToString() + LatZone + " " + (int)easting + "mE " + (int)northing + "mN"; - } - - private static Coordinate UTMtoLatLong(double x, double y, double zone, double equatorialRadius, double flattening) - { - //x easting - //y northing - - //http://home.hiwaay.net/~taylorc/toolbox/geography/geoutm.html - double phif, Nf, Nfpow, nuf2, ep2, tf, tf2, tf4, cf; - double x1frac, x2frac, x3frac, x4frac, x5frac, x6frac, x7frac, x8frac; - double x2poly, x3poly, x4poly, x5poly, x6poly, x7poly, x8poly; - - double sm_a = equatorialRadius; - double sm_b = equatorialRadius * (1 - (1.0 / flattening)); //Polar Radius - - /* Get the value of phif, the footpoint latitude. */ - phif = FootpointLatitude(y,equatorialRadius,flattening); - - /* Precalculate ep2 */ - ep2 = (Math.Pow(sm_a, 2.0) - Math.Pow(sm_b, 2.0)) - / Math.Pow(sm_b, 2.0); - - /* Precalculate cos (phif) */ - cf = Math.Cos(phif); - - /* Precalculate nuf2 */ - nuf2 = ep2 * Math.Pow(cf, 2.0); - - /* Precalculate Nf and initialize Nfpow */ - Nf = Math.Pow(sm_a, 2.0) / (sm_b * Math.Sqrt(1 + nuf2)); - Nfpow = Nf; - - /* Precalculate tf */ - tf = Math.Tan(phif); - tf2 = tf * tf; - tf4 = tf2 * tf2; - - /* Precalculate fractional coefficients for x**n in the equations - below to simplify the expressions for latitude and longitude. */ - x1frac = 1.0 / (Nfpow * cf); - - Nfpow *= Nf; /* now equals Nf**2) */ - x2frac = tf / (2.0 * Nfpow); - - Nfpow *= Nf; /* now equals Nf**3) */ - x3frac = 1.0 / (6.0 * Nfpow * cf); - - Nfpow *= Nf; /* now equals Nf**4) */ - x4frac = tf / (24.0 * Nfpow); - - Nfpow *= Nf; /* now equals Nf**5) */ - x5frac = 1.0 / (120.0 * Nfpow * cf); - - Nfpow *= Nf; /* now equals Nf**6) */ - x6frac = tf / (720.0 * Nfpow); - - Nfpow *= Nf; /* now equals Nf**7) */ - x7frac = 1.0 / (5040.0 * Nfpow * cf); - - Nfpow *= Nf; /* now equals Nf**8) */ - x8frac = tf / (40320.0 * Nfpow); - - /* Precalculate polynomial coefficients for x**n. - -- x**1 does not have a polynomial coefficient. */ - x2poly = -1.0 - nuf2; - - x3poly = -1.0 - 2 * tf2 - nuf2; - - x4poly = 5.0 + 3.0 * tf2 + 6.0 * nuf2 - 6.0 * tf2 * nuf2 - - 3.0 * (nuf2 * nuf2) - 9.0 * tf2 * (nuf2 * nuf2); - - x5poly = 5.0 + 28.0 * tf2 + 24.0 * tf4 + 6.0 * nuf2 + 8.0 * tf2 * nuf2; - - x6poly = -61.0 - 90.0 * tf2 - 45.0 * tf4 - 107.0 * nuf2 - + 162.0 * tf2 * nuf2; - - x7poly = -61.0 - 662.0 * tf2 - 1320.0 * tf4 - 720.0 * (tf4 * tf2); - - x8poly = 1385.0 + 3633.0 * tf2 + 4095.0 * tf4 + 1575 * (tf4 * tf2); - - /* Calculate latitude */ - double nLat = phif + x2frac * x2poly * (x * x) - + x4frac * x4poly * Math.Pow(x, 4.0) - + x6frac * x6poly * Math.Pow(x, 6.0) - + x8frac * x8poly * Math.Pow(x, 8.0); - - /* Calculate longitude */ - double nLong = zone + x1frac * x - + x3frac * x3poly * Math.Pow(x, 3.0) - + x5frac * x5poly * Math.Pow(x, 5.0) - + x7frac * x7poly * Math.Pow(x, 7.0); - - double dLat = RadToDeg(nLat); - double dLong = RadToDeg(nLong); - if (dLat > 90) { dLat = 90; } - if (dLat < -90) { dLat = -90; } - if (dLong > 180) { dLong = 180; } - if (dLong < -180) { dLong = -180; } - - Coordinate c = new Coordinate(equatorialRadius,flattening, true); - CoordinatePart cLat = new CoordinatePart(dLat, CoordinateType.Lat); - CoordinatePart cLng = new CoordinatePart(dLong, CoordinateType.Long); - - c.Latitude = cLat; - c.Longitude = cLng; - - return c; - } - - private static double RadToDeg(double rad) - { - double pi = 3.14159265358979; - return (rad / pi * 180.0); - } - private static double DegToRad(double deg) - { - double pi = 3.14159265358979; - return (deg / 180.0 * pi); - } - private static double FootpointLatitude(double y, double equatorialRadius, double flattening) - { - double y_, alpha_, beta_, gamma_, delta_, epsilon_, n; - double result; - - - /* Ellipsoid model constants (actual values here are for WGS84) */ - double sm_a = equatorialRadius; - double sm_b = equatorialRadius * (1 - (1.0 / flattening)); - - - /* Precalculate n (Eq. 10.18) */ - n = (sm_a - sm_b) / (sm_a + sm_b); - - /* Precalculate alpha_ (Eq. 10.22) */ - /* (Same as alpha in Eq. 10.17) */ - alpha_ = ((sm_a + sm_b) / 2.0) * (1 + (Math.Pow(n, 2.0) / 4) + (Math.Pow(n, 4.0) / 64)); - - /* Precalculate y_ (Eq. 10.23) */ - y_ = y / alpha_; - - /* Precalculate beta_ (Eq. 10.22) */ - beta_ = (3.0 * n / 2.0) + (-27.0 * Math.Pow(n, 3.0) / 32.0) - + (269.0 * Math.Pow(n, 5.0) / 512.0); - - /* Precalculate gamma_ (Eq. 10.22) */ - gamma_ = (21.0 * Math.Pow(n, 2.0) / 16.0) - + (-55.0 * Math.Pow(n, 4.0) / 32.0); - - /* Precalculate delta_ (Eq. 10.22) */ - delta_ = (151.0 * Math.Pow(n, 3.0) / 96.0) - + (-417.0 * Math.Pow(n, 5.0) / 128.0); - - /* Precalculate epsilon_ (Eq. 10.22) */ - epsilon_ = (1097.0 * Math.Pow(n, 4.0) / 512.0); - - /* Now calculate the sum of the series (Eq. 10.21) */ - result = y_ + (beta_ * Math.Sin(2.0 * y_)) - + (gamma_ * Math.Sin(4.0 * y_)) - + (delta_ * Math.Sin(6.0 * y_)) - + (epsilon_ * Math.Sin(8.0 * y_)); - - return result; - } - - /// - /// Converts UTM coordinate to Lat/Long - /// - /// utm - /// Coordinate object - public static Coordinate ConvertUTMtoLatLong(UniversalTransverseMercator utm) - { - - bool southhemi = false; - if (utm.latZone == "A" || utm.latZone == "B" || utm.latZone == "C" || utm.latZone == "D" || utm.latZone == "E" || utm.latZone == "F" || utm.latZone == "G" || utm.latZone == "H" || utm.latZone == "J" || - utm.latZone == "K" || utm.latZone == "L" || utm.latZone == "M") - { - southhemi = true; - } - - double cmeridian; - - double x = utm.Easting - 500000.0; - double UTMScaleFactor = 0.9996; - x /= UTMScaleFactor; - - /* If in southern hemisphere, adjust y accordingly. */ - double y = utm.Northing; - if (southhemi) - { - y -= 10000000.0; - } - - y /= UTMScaleFactor; - - cmeridian = UTMCentralMeridian(utm.LongZone); - - Coordinate c = UTMtoLatLong(x, y, cmeridian, utm.equatorial_radius, utm.inverse_flattening); - - if (c.Latitude.ToDouble() > 85 || c.Latitude.ToDouble() < -85) - { - Debug.WriteLine("UTM conversions greater than 85 degrees or less than -85 degree latitude contain major deviations and should be used with caution."); - } - return c; - - - } - - private static double UTMCentralMeridian(double zone) - { - double cmeridian; - - cmeridian = DegToRad(-183.0 + (zone * 6.0)); - - return cmeridian; - } - - /// - /// Property changed event - /// - public event PropertyChangedEventHandler PropertyChanged; - /// - /// Notify property changed - /// - /// Property name - public void NotifyPropertyChanged(string propName) - { - if (this.PropertyChanged != null) - { - PropertyChanged(this, new PropertyChangedEventArgs(propName)); - } - } - } - + } + } + + /// + /// Datum Equatorial Radius / Semi Major Axis + /// + public Double Equatorial_Radius => this.equatorial_radius; + /// + /// Datum Flattening + /// + public Double Inverse_Flattening => this.inverse_flattening; + + /// + /// Is the UTM conversion within the coordinate system's accurate boundaries after conversion from Lat/Long. + /// + public Boolean WithinCoordinateSystemBounds { get; private set; } = true; + + /// + /// Constructs a UTM object based off DD Lat/Long + /// + /// DD Latitude + /// DD Longitide + /// Parent Coordinate Object + internal UniversalTransverseMercator(Double lat, Double longi, Coordinate _) { + //validate coords + + //if (lat > 180) { throw new ArgumentOutOfRangeException("Degrees out of range", "Longitudinal coordinate decimal cannot be greater than 180."); } + //if (lat < -180) { throw new ArgumentOutOfRangeException("Degrees out of range", "Longitudinal coordinate decimal cannot be less than 180."); } + + //if (longi > 90) { throw new ArgumentOutOfRangeException("Degrees out of range", "Latitudinal coordinate decimal cannot be greater than 90."); } + //if (longi < -90) { throw new ArgumentOutOfRangeException("Degrees out of range", "Latitudinal coordinate decimal cannot be less than 90."); } + this.equatorial_radius = 6378137.0; + this.inverse_flattening = 298.257223563; + this.ToUTM(lat, longi, this); + + //this.coordinate = c; + } + /// + /// Constructs a UTM object based off DD Lat/Long + /// + /// DD Latitude + /// DD Longitide + /// Parent Coordinate Object + /// Equatorial Radius + /// Flattening + internal UniversalTransverseMercator(Double lat, Double longi, Coordinate _, Double rad, Double flt) { + this.equatorial_radius = rad; + this.inverse_flattening = flt; + this.ToUTM(lat, longi, this); + + //this.coordinate = c; + } + /// + /// Constructs a UTM object based off a UTM coordinate + /// Not yet implemented + /// + /// Zone Letter + /// Zone Number + /// Easting + /// Northing + /// Parent Coordinate Object + /// Equatorial Radius + /// Inverse Flattening + internal UniversalTransverseMercator(String latz, Int32 longz, Double e, Double n, Coordinate c, Double rad, Double flt) { + //validate utm + if (longz < 1 || longz > 60) { Debug.WriteLine("Longitudinal zone out of range", "UTM longitudinal zones must be between 1-60."); } + if (!this.Verify_Lat_Zone(latz)) { throw new ArgumentException("Latitudinal zone invalid", "UTM latitudinal zone was unrecognized."); } + if (e < 160000 || e > 834000) { Debug.WriteLine("The Easting value provided is outside the max allowable range. If this is intentional, use with caution."); } + if (n < 0 || n > 10000000) { throw new ArgumentOutOfRangeException("Northing out of range", "Northing must be between 0-10,000,000."); } + this.equatorial_radius = rad; + this.inverse_flattening = flt; + this.latZone = latz; + this.longZone = longz; + + this.easting = e; + this.northing = n; + + //this.coordinate = c; + this.WithinCoordinateSystemBounds = c.Latitude.DecimalDegree > -80 && c.Latitude.DecimalDegree < 84; + } + + /// + /// Verifies Lat zone when convert from UTM to DD Lat/Long + /// + /// Zone Letter + /// boolean + private Boolean Verify_Lat_Zone(String l) => LatZones.longZongLetters.Where(x => x == l.ToUpper()).Count() == 1; + //private Double degreeToRadian(Double degree) => degree * Math.PI / 180; + /// + /// Assigns UTM values based of Lat/Long + /// + /// DD Latitude + /// DD longitude + /// UTM Object to modify + internal void ToUTM(Double lat, Double longi, UniversalTransverseMercator utm) { + Int32 zone = (Int32)Math.Floor(longi / 6 + 31); + String letter = lat < -72 ? "C" + : lat < -64 ? "D" + : lat < -56 ? "E" + : lat < -48 ? "F" + : lat < -40 ? "G" + : lat < -32 ? "H" + : lat < -24 ? "J" + : lat < -16 ? "K" + : lat < -8 ? "L" + : lat < 0 ? "M" + : lat < 8 ? "N" + : lat < 16 ? "P" + : lat < 24 ? "Q" + : lat < 32 ? "R" + : lat < 40 ? "S" + : lat < 48 ? "T" + : lat < 56 ? "U" + : lat < 64 ? "V" + : lat < 72 ? "W" : "X"; + Double a = utm.equatorial_radius; + Double f = 1.0 / utm.inverse_flattening; + Double b = a * (1 - f); // polar radius + + Double e = Math.Sqrt(1 - Math.Pow(b, 2) / Math.Pow(a, 2)); + _ = e / Math.Sqrt(1 - Math.Pow(e, 1)); + + Double drad = Math.PI / 180; + Double k0 = 0.9996; + + Double phi = lat * drad; // convert latitude to radians + _ = longi * drad; // convert longitude to radians + Double utmz = 1 + Math.Floor((longi + 180) / 6.0); // longitude to utm zone + Double zcm = 3 + 6.0 * (utmz - 1) - 180; // central meridian of a zone + // this gives us zone A-B for below 80S + Double esq = 1 - b / a * (b / a); + Double e0sq = e * e / (1 - Math.Pow(e, 2)); + Double N = a / Math.Sqrt(1 - Math.Pow(e * Math.Sin(phi), 2)); + Double T = Math.Pow(Math.Tan(phi), 2); + Double C = e0sq * Math.Pow(Math.Cos(phi), 2); + Double A = (longi - zcm) * drad * Math.Cos(phi); + + // calculate M (USGS style) + Double M = phi * (1 - esq * (1.0 / 4.0 + esq * (3.0 / 64.0 + 5.0 * esq / 256.0))); + M -= Math.Sin(2.0 * phi) * (esq * (3.0 / 8.0 + esq * (3.0 / 32.0 + 45.0 * esq / 1024.0))); + M += Math.Sin(4.0 * phi) * (esq * esq * (15.0 / 256.0 + esq * 45.0 / 1024.0)); + M -= Math.Sin(6.0 * phi) * (esq * esq * esq * (35.0 / 3072.0)); + M *= a;//Arc length along standard meridian + + Double M0 = 0;// if another point of origin is used than the equator + + // Calculate the UTM values... + // first the easting + Double x = k0 * N * A * (1 + A * A * ((1 - T + C) / 6 + A * A * (5 - 18 * T + T * T + 72.0 * C - 58 * e0sq) / 120.0)); //Easting relative to CM + x += 500000; // standard easting + + // Northing + + Double y = k0 * (M - M0 + N * Math.Tan(phi) * (A * A * (1 / 2.0 + A * A * ((5 - T + 9 * C + 4 * C * C) / 24.0 + A * A * (61 - 58 * T + T * T + 600 * C - 330 * e0sq) / 720.0)))); // first from the equator + _ = y + 10000000; //yg = y global, from S. Pole + if (y < 0) { + y = 10000000 + y; // add in false northing if south of the equator + } + + + Double easting = Math.Round(10 * x) / 10.0; + Double northing = Math.Round(10 * y) / 10.0; + + utm.latZone = letter; + utm.longZone = zone; + utm.easting = easting; + utm.northing = northing; + + this.WithinCoordinateSystemBounds = lat > -80 && lat < 84; + } + + /// + /// UTM Default String Format + /// + /// UTM Formatted Coordinate String + public override String ToString() => !this.WithinCoordinateSystemBounds ? "" : this.longZone.ToString() + this.LatZone + " " + (Int32)this.easting + "mE " + (Int32)this.northing + "mN"; + //MGRS Coordinate is outside its reliable boundaries. Return empty. + + private static Coordinate UTMtoLatLong(Double x, Double y, Double zone, Double equatorialRadius, Double flattening) { + //x easting + //y northing + + //http://home.hiwaay.net/~taylorc/toolbox/geography/geoutm.html + Double phif, Nf, Nfpow, nuf2, ep2, tf, tf2, tf4, cf; + Double x1frac, x2frac, x3frac, x4frac, x5frac, x6frac, x7frac, x8frac; + Double x2poly, x3poly, x4poly, x5poly, x6poly, x7poly, x8poly; + + Double sm_a = equatorialRadius; + Double sm_b = equatorialRadius * (1 - 1.0 / flattening); //Polar Radius + + /* Get the value of phif, the footpoint latitude. */ + phif = FootpointLatitude(y, equatorialRadius, flattening); + + /* Precalculate ep2 */ + ep2 = (Math.Pow(sm_a, 2.0) - Math.Pow(sm_b, 2.0)) / Math.Pow(sm_b, 2.0); + + /* Precalculate cos (phif) */ + cf = Math.Cos(phif); + + /* Precalculate nuf2 */ + nuf2 = ep2 * Math.Pow(cf, 2.0); + + /* Precalculate Nf and initialize Nfpow */ + Nf = Math.Pow(sm_a, 2.0) / (sm_b * Math.Sqrt(1 + nuf2)); + Nfpow = Nf; + + /* Precalculate tf */ + tf = Math.Tan(phif); + tf2 = tf * tf; + tf4 = tf2 * tf2; + + /* Precalculate fractional coefficients for x**n in the equations + below to simplify the expressions for latitude and longitude. */ + x1frac = 1.0 / (Nfpow * cf); + + Nfpow *= Nf; /* now equals Nf**2) */ + x2frac = tf / (2.0 * Nfpow); + + Nfpow *= Nf; /* now equals Nf**3) */ + x3frac = 1.0 / (6.0 * Nfpow * cf); + + Nfpow *= Nf; /* now equals Nf**4) */ + x4frac = tf / (24.0 * Nfpow); + + Nfpow *= Nf; /* now equals Nf**5) */ + x5frac = 1.0 / (120.0 * Nfpow * cf); + + Nfpow *= Nf; /* now equals Nf**6) */ + x6frac = tf / (720.0 * Nfpow); + + Nfpow *= Nf; /* now equals Nf**7) */ + x7frac = 1.0 / (5040.0 * Nfpow * cf); + + Nfpow *= Nf; /* now equals Nf**8) */ + x8frac = tf / (40320.0 * Nfpow); + + /* Precalculate polynomial coefficients for x**n. + -- x**1 does not have a polynomial coefficient. */ + x2poly = -1.0 - nuf2; + + x3poly = -1.0 - 2 * tf2 - nuf2; + + x4poly = 5.0 + 3.0 * tf2 + 6.0 * nuf2 - 6.0 * tf2 * nuf2 - 3.0 * (nuf2 * nuf2) - 9.0 * tf2 * (nuf2 * nuf2); + + x5poly = 5.0 + 28.0 * tf2 + 24.0 * tf4 + 6.0 * nuf2 + 8.0 * tf2 * nuf2; + + x6poly = -61.0 - 90.0 * tf2 - 45.0 * tf4 - 107.0 * nuf2 + 162.0 * tf2 * nuf2; + + x7poly = -61.0 - 662.0 * tf2 - 1320.0 * tf4 - 720.0 * (tf4 * tf2); + + x8poly = 1385.0 + 3633.0 * tf2 + 4095.0 * tf4 + 1575 * (tf4 * tf2); + + /* Calculate latitude */ + Double nLat = phif + x2frac * x2poly * (x * x) + x4frac * x4poly * Math.Pow(x, 4.0) + x6frac * x6poly * Math.Pow(x, 6.0) + x8frac * x8poly * Math.Pow(x, 8.0); + + /* Calculate longitude */ + Double nLong = zone + x1frac * x + x3frac * x3poly * Math.Pow(x, 3.0) + x5frac * x5poly * Math.Pow(x, 5.0) + x7frac * x7poly * Math.Pow(x, 7.0); + + Double dLat = RadToDeg(nLat); + Double dLong = RadToDeg(nLong); + if (dLat > 90) { dLat = 90; } + if (dLat < -90) { dLat = -90; } + if (dLong > 180) { dLong = 180; } + if (dLong < -180) { dLong = -180; } + + Coordinate c = new Coordinate(equatorialRadius, flattening, true); + CoordinatePart cLat = new CoordinatePart(dLat, CoordinateType.Lat); + CoordinatePart cLng = new CoordinatePart(dLong, CoordinateType.Long); + + c.Latitude = cLat; + c.Longitude = cLng; + + return c; + } + + private static Double RadToDeg(Double rad) { + Double pi = 3.14159265358979; + return rad / pi * 180.0; + } + private static Double DegToRad(Double deg) { + Double pi = 3.14159265358979; + return deg / 180.0 * pi; + } + private static Double FootpointLatitude(Double y, Double equatorialRadius, Double flattening) { + Double y_, alpha_, beta_, gamma_, delta_, epsilon_, n; + Double result; + + + /* Ellipsoid model constants (actual values here are for WGS84) */ + Double sm_a = equatorialRadius; + Double sm_b = equatorialRadius * (1 - 1.0 / flattening); + + + /* Precalculate n (Eq. 10.18) */ + n = (sm_a - sm_b) / (sm_a + sm_b); + + /* Precalculate alpha_ (Eq. 10.22) */ + /* (Same as alpha in Eq. 10.17) */ + alpha_ = (sm_a + sm_b) / 2.0 * (1 + Math.Pow(n, 2.0) / 4 + Math.Pow(n, 4.0) / 64); + + /* Precalculate y_ (Eq. 10.23) */ + y_ = y / alpha_; + + /* Precalculate beta_ (Eq. 10.22) */ + beta_ = 3.0 * n / 2.0 + -27.0 * Math.Pow(n, 3.0) / 32.0 + 269.0 * Math.Pow(n, 5.0) / 512.0; + + /* Precalculate gamma_ (Eq. 10.22) */ + gamma_ = 21.0 * Math.Pow(n, 2.0) / 16.0 + -55.0 * Math.Pow(n, 4.0) / 32.0; + + /* Precalculate delta_ (Eq. 10.22) */ + delta_ = 151.0 * Math.Pow(n, 3.0) / 96.0 + -417.0 * Math.Pow(n, 5.0) / 128.0; + + /* Precalculate epsilon_ (Eq. 10.22) */ + epsilon_ = 1097.0 * Math.Pow(n, 4.0) / 512.0; + + /* Now calculate the sum of the series (Eq. 10.21) */ + result = y_ + beta_ * Math.Sin(2.0 * y_) + gamma_ * Math.Sin(4.0 * y_) + delta_ * Math.Sin(6.0 * y_) + epsilon_ * Math.Sin(8.0 * y_); + + return result; + } + + /// + /// Converts UTM coordinate to Lat/Long + /// + /// utm + /// Coordinate object + public static Coordinate ConvertUTMtoLatLong(UniversalTransverseMercator utm) { + + Boolean southhemi = false; + if (utm.latZone == "A" || utm.latZone == "B" || utm.latZone == "C" || utm.latZone == "D" || utm.latZone == "E" || utm.latZone == "F" || utm.latZone == "G" || utm.latZone == "H" || utm.latZone == "J" || utm.latZone == "K" || utm.latZone == "L" || utm.latZone == "M") { + southhemi = true; + } + + Double cmeridian; + + Double x = utm.Easting - 500000.0; + Double UTMScaleFactor = 0.9996; + x /= UTMScaleFactor; + + /* If in southern hemisphere, adjust y accordingly. */ + Double y = utm.Northing; + if (southhemi) { + y -= 10000000.0; + } + + y /= UTMScaleFactor; + + cmeridian = UTMCentralMeridian(utm.LongZone); + + Coordinate c = UTMtoLatLong(x, y, cmeridian, utm.equatorial_radius, utm.inverse_flattening); + + if (c.Latitude.ToDouble() > 85 || c.Latitude.ToDouble() < -85) { + Debug.WriteLine("UTM conversions greater than 85 degrees or less than -85 degree latitude contain major deviations and should be used with caution."); + } + return c; + + + } + + private static Double UTMCentralMeridian(Double zone) { + Double cmeridian; + + cmeridian = DegToRad(-183.0 + zone * 6.0); + + return cmeridian; + } + + /// + /// Property changed event + /// + public event PropertyChangedEventHandler PropertyChanged; + /// + /// Notify property changed + /// + /// Property name + public void NotifyPropertyChanged(String propName) { + if (this.PropertyChanged != null) { + PropertyChanged(this, new PropertyChangedEventArgs(propName)); + } + } + } + } diff --git a/CoordinateSharp/Coordinate.cs b/CoordinateSharp/Coordinate.cs index d03d76c..af37e20 100644 --- a/CoordinateSharp/Coordinate.cs +++ b/CoordinateSharp/Coordinate.cs @@ -34,8 +34,7 @@ SOFTWARE. using System; using System.ComponentModel; -namespace CoordinateSharp -{ +namespace CoordinateSharp { /// /// Observable class for handling all location based information. /// This is the main class for CoordinateSharp. @@ -44,16 +43,14 @@ namespace CoordinateSharp /// All information should be pulled from this class to include celestial information /// [Serializable] - public class Coordinate : INotifyPropertyChanged - { + public class Coordinate : INotifyPropertyChanged { /// /// Creates an empty Coordinate. /// /// /// Values will need to be provided to latitude/longitude CoordinateParts manually /// - public Coordinate() - { + public Coordinate() { this.FormatOptions = new CoordinateFormatOptions(); this.geoDate = new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc); this.latitude = new CoordinatePart(CoordinateType.Lat); @@ -77,8 +74,7 @@ namespace CoordinateSharp /// /// Values will need to be provided to latitude/longitude CoordinateParts manually /// - internal Coordinate(Double equatorialRadius, Double inverseFlattening, Boolean t) - { + internal Coordinate(Double equatorialRadius, Double inverseFlattening, Boolean _) { this.FormatOptions = new CoordinateFormatOptions(); this.geoDate = new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc); this.latitude = new CoordinatePart(CoordinateType.Lat); @@ -102,8 +98,7 @@ namespace CoordinateSharp /// /// Geodate will default to 1/1/1900 GMT until provided /// - public Coordinate(Double lat, Double longi) - { + public Coordinate(Double lat, Double longi) { this.FormatOptions = new CoordinateFormatOptions(); this.geoDate = new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc); this.latitude = new CoordinatePart(lat, CoordinateType.Lat); @@ -126,8 +121,7 @@ namespace CoordinateSharp /// latitude /// longitude /// DateTime (UTC) - public Coordinate(Double lat, Double longi, DateTime date) - { + public Coordinate(Double lat, Double longi, DateTime date) { this.FormatOptions = new CoordinateFormatOptions(); this.latitude = new CoordinatePart(lat, CoordinateType.Lat); this.longitude = new CoordinatePart(longi, CoordinateType.Long); @@ -152,8 +146,7 @@ namespace CoordinateSharp /// Values will need to be provided to latitude/longitude manually /// /// Eager loading options - public Coordinate(EagerLoad eagerLoad) - { + public Coordinate(EagerLoad eagerLoad) { this.FormatOptions = new CoordinateFormatOptions(); this.geoDate = new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc); this.latitude = new CoordinatePart(CoordinateType.Lat); @@ -188,8 +181,7 @@ namespace CoordinateSharp /// latitude /// longitude /// Eager loading options - public Coordinate(Double lat, Double longi, EagerLoad eagerLoad) - { + public Coordinate(Double lat, Double longi, EagerLoad eagerLoad) { this.FormatOptions = new CoordinateFormatOptions(); this.geoDate = new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc); this.latitude = new CoordinatePart(lat, CoordinateType.Lat); @@ -223,8 +215,7 @@ namespace CoordinateSharp /// Decimal format longitude /// DateTime you wish to use for celestial calculation /// Eager loading options - public Coordinate(Double lat, Double longi, DateTime date, EagerLoad eagerLoad) - { + public Coordinate(Double lat, Double longi, DateTime date, EagerLoad eagerLoad) { this.FormatOptions = new CoordinateFormatOptions(); this.latitude = new CoordinatePart(lat, CoordinateType.Lat); this.longitude = new CoordinatePart(longi, CoordinateType.Long); @@ -261,8 +252,7 @@ namespace CoordinateSharp /// /// Latitudinal Coordinate Part /// - public CoordinatePart Latitude - { + public CoordinatePart Latitude { get => this.latitude; set { if (this.latitude != value) { @@ -292,8 +282,7 @@ namespace CoordinateSharp /// /// Longitudinal Coordinate Part /// - public CoordinatePart Longitude - { + public CoordinatePart Longitude { get => this.longitude; set { if (this.longitude != value) { @@ -325,8 +314,7 @@ namespace CoordinateSharp /// /// Assumes all times are in UTC /// - public DateTime GeoDate - { + public DateTime GeoDate { get => this.geoDate; set { if (this.geoDate != value) { @@ -357,8 +345,7 @@ namespace CoordinateSharp /// Uses Ellipsoidal height with no geoid model included. /// 0 = Mean Sea Level based on the provided Datum. /// - public ECEF ECEF - { + public ECEF ECEF { get => this.ecef; //Required due to GeoDetic Height @@ -376,8 +363,7 @@ namespace CoordinateSharp /// Used to determine what format the coordinate was parsed from. /// Will equal "None" if Coordinate was not initialzed via a TryParse() method. /// - public Parse_Format_Type Parse_Format - { + public Parse_Format_Type Parse_Format { get => this.parse_Format; internal set { if (this.parse_Format != value) { @@ -399,8 +385,7 @@ namespace CoordinateSharp /// /// Initialize UTM and MGRS information (required if eager loading is turned off). /// - public void LoadUTM_MGRS_Info() - { + public void LoadUTM_MGRS_Info() { this.UTM = new UniversalTransverseMercator(this.latitude.ToDouble(), this.longitude.ToDouble(), this); this.MGRS = new MilitaryGridReferenceSystem(this.UTM); } @@ -431,8 +416,7 @@ namespace CoordinateSharp /// Overridden Coordinate ToString() method. /// /// string (formatted). - public override String ToString() - { + public override String ToString() { String latString = this.latitude.ToString(); String longSting = this.longitude.ToString(); return latString + " " + longSting; @@ -444,8 +428,7 @@ namespace CoordinateSharp /// /// CoordinateFormatOptions /// Custom formatted coordinate - public String ToString(CoordinateFormatOptions options) - { + public String ToString(CoordinateFormatOptions options) { String latString = this.latitude.ToString(options); String longSting = this.longitude.ToString(options); return latString + " " + longSting; @@ -458,8 +441,7 @@ namespace CoordinateSharp /// /// Equatorial Radius /// Inverse Flattening - public void Set_Datum(Double radius, Double flat) - { + public void Set_Datum(Double radius, Double flat) { //WGS84 //RADIUS 6378137.0; //FLATTENING 298.257223563; @@ -487,8 +469,7 @@ namespace CoordinateSharp /// Equatorial Radius /// Inverse Flattening /// Coordinate_Datum - public void Set_Datum(Double radius, Double flat, Coordinate_Datum cd) - { + public void Set_Datum(Double radius, Double flat, Coordinate_Datum cd) { //WGS84 //RADIUS 6378137.0; //FLATTENING 298.257223563; @@ -554,8 +535,7 @@ namespace CoordinateSharp /// //New Coordinate - N 25º 4' 54.517" E 24º 57' 29.189" /// /// - public void Move(Double distance, Double bearing, Shape shape) - { + public void Move(Double distance, Double bearing, Shape shape) { //Convert to Radians for formula Double lat1 = this.latitude.ToRadians(); Double lon1 = this.longitude.ToRadians(); @@ -605,8 +585,7 @@ namespace CoordinateSharp /// //New Coordinate - N 24º 56' 21.526" E 25º 4' 23.944" /// /// - public void Move(Coordinate target, Double distance, Shape shape) - { + public void Move(Coordinate target, Double distance, Shape shape) { Distance d = new Distance(this, target, shape); //Convert to Radians for formula Double lat1 = this.latitude.ToRadians(); @@ -656,8 +635,7 @@ namespace CoordinateSharp /// //New Coordinate - N 25º 4' 54.517" E 24º 57' 29.189" /// /// - public void Move(Distance distance, Double bearing, Shape shape) - { + public void Move(Distance distance, Double bearing, Shape shape) { //Convert to Radians for formula Double lat1 = this.latitude.ToRadians(); Double lon1 = this.longitude.ToRadians(); @@ -708,8 +686,7 @@ namespace CoordinateSharp /// //New Coordinate - N 24º 56' 21.526" E 25º 4' 23.944" /// /// - public void Move(Coordinate target, Distance distance, Shape shape) - { + public void Move(Coordinate target, Distance distance, Shape shape) { Distance d = new Distance(this, target, shape); //Convert to Radians for formula Double lat1 = this.latitude.ToRadians(); @@ -752,9 +729,7 @@ namespace CoordinateSharp /// } /// /// - public static Boolean TryParse(String s, out Coordinate c) - { - c = null; + public static Boolean TryParse(String s, out Coordinate c) { if (FormatFinder.TryParse(s, CartesianType.Cartesian, out c)) { Parse_Format_Type pft = c.Parse_Format; c = new Coordinate(c.Latitude.ToDouble(), c.Longitude.ToDouble()) { @@ -781,9 +756,7 @@ namespace CoordinateSharp /// } /// /// - public static Boolean TryParse(String s, DateTime geoDate, out Coordinate c) - { - c = null; + public static Boolean TryParse(String s, DateTime geoDate, out Coordinate c) { if (FormatFinder.TryParse(s, CartesianType.Cartesian, out c)) { Parse_Format_Type pft = c.Parse_Format; c = new Coordinate(c.Latitude.ToDouble(), c.Longitude.ToDouble(), geoDate) { @@ -810,9 +783,7 @@ namespace CoordinateSharp /// } /// /// - public static Boolean TryParse(String s, CartesianType ct, out Coordinate c) - { - c = null; + public static Boolean TryParse(String s, CartesianType ct, out Coordinate c) { if (FormatFinder.TryParse(s, ct, out c)) { Parse_Format_Type pft = c.Parse_Format; if (ct == CartesianType.ECEF) { @@ -845,9 +816,7 @@ namespace CoordinateSharp /// } /// /// - public static Boolean TryParse(String s, DateTime geoDate, CartesianType ct, out Coordinate c) - { - c = null; + public static Boolean TryParse(String s, DateTime geoDate, CartesianType ct, out Coordinate c) { if (FormatFinder.TryParse(s, ct, out c)) { Parse_Format_Type pft = c.Parse_Format; if (ct == CartesianType.ECEF) { @@ -872,8 +841,7 @@ namespace CoordinateSharp /// Notify property changed /// /// Property name - public void NotifyPropertyChanged(String propName) - { + public void NotifyPropertyChanged(String propName) { switch (propName) { case "CelestialInfo": if (!this.EagerLoadSettings.Celestial || this.CelestialInfo == null) { return; } //Prevent Null Exceptions and calls while eagerloading is off @@ -913,8 +881,7 @@ namespace CoordinateSharp /// Objects can be passed to Coordinate object Latitude and Longitude properties. /// [Serializable] - public class CoordinatePart : INotifyPropertyChanged - { + public class CoordinatePart : INotifyPropertyChanged { //Defaults: //Format: Degrees Minutes Seconds //Rounding: Dependent upon selected format @@ -941,8 +908,7 @@ namespace CoordinateSharp /// /// Observable decimal format coordinate. /// - public Double DecimalDegree - { + public Double DecimalDegree { get => this.decimalDegree; set { //If changing, notify the needed property changes @@ -1018,8 +984,7 @@ namespace CoordinateSharp /// /// Observable decimal format minute. /// - public Double DecimalMinute - { + public Double DecimalMinute { get => this.decimalMinute; set { if (this.decimalMinute != value) { @@ -1048,7 +1013,7 @@ namespace CoordinateSharp Decimal newDM = decValue / 60; //divide decimalMinute by 60 to get storage value Decimal newDD = this.degrees + newDM;//Add new decimal value to the floor degree value to get new decimalDegree; - if (this.decimalDegree < 0) { newDD = newDD * -1; } //Restore negative if needed + if (this.decimalDegree < 0) { newDD *= -1; } //Restore negative if needed this.decimalDegree = Convert.ToDouble(newDD); //Convert back to double for storage @@ -1064,8 +1029,7 @@ namespace CoordinateSharp /// /// Observable coordinate degree. /// - public Int32 Degrees - { + public Int32 Degrees { get => this.degrees; set { //Validate Value @@ -1105,8 +1069,7 @@ namespace CoordinateSharp /// /// Observable coordinate minute. /// - public Int32 Minutes - { + public Int32 Minutes { get => this.minutes; set { if (this.minutes != value) { @@ -1114,9 +1077,9 @@ namespace CoordinateSharp //Validate the minutes Decimal vMin = Convert.ToDecimal(value); if (this.type == CoordinateType.Lat) { - if (this.degrees + (vMin / 60) > 90) { throw new ArgumentOutOfRangeException("Degrees out of range", "Latitudinal degrees cannot be greater than 90"); } + if (this.degrees + vMin / 60 > 90) { throw new ArgumentOutOfRangeException("Degrees out of range", "Latitudinal degrees cannot be greater than 90"); } } else { - if (this.degrees + (vMin / 60) > 180) { throw new ArgumentOutOfRangeException("Degrees out of range", "Longitudinal degrees cannot be greater than 180"); } + if (this.degrees + vMin / 60 > 180) { throw new ArgumentOutOfRangeException("Degrees out of range", "Longitudinal degrees cannot be greater than 180"); } } if (value >= 60) { throw new ArgumentOutOfRangeException("Minutes out of range", "Minutes cannot be greater than or equal to 60"); @@ -1154,8 +1117,7 @@ namespace CoordinateSharp /// /// Observable coordinate second. /// - public Double Seconds - { + public Double Seconds { get => this.seconds; set { if (value < 0) { value *= -1; }//Adjust accidental negative input @@ -1182,9 +1144,9 @@ namespace CoordinateSharp this.seconds = value; - Double degABS = Math.Abs(this.decimalDegree); //Make decimalDegree positive - Double degFloor = Math.Truncate(degABS); //Truncate the number left of the decimal - Decimal f = Convert.ToDecimal(degFloor); //Convert to decimal to keep precision + //Double degABS = Math.Abs(this.decimalDegree); //Make decimalDegree positive + //Double degFloor = Math.Truncate(degABS); //Truncate the number left of the decimal + //Decimal f = Convert.ToDecimal(degFloor); //Convert to decimal to keep precision Decimal secs = Convert.ToDecimal(this.seconds); //Convert seconds to decimal for calculations secs /= 60; //Convert to storage format @@ -1207,8 +1169,7 @@ namespace CoordinateSharp /// /// Observable coordinate position. /// - public CoordinatesPosition Position - { + public CoordinatesPosition Position { get => this.position; set { if (this.position != value) { @@ -1231,8 +1192,7 @@ namespace CoordinateSharp /// CoordinateType /// Parent Coordinate object [Obsolete("Method is deprecated. You no longer need to pass a Coordinate object through the constructor.")] - public CoordinatePart(CoordinateType t, Coordinate c) - { + public CoordinatePart(CoordinateType t, Coordinate c) { this.parent = c; this.type = t; this.decimalDegree = 0; @@ -1248,8 +1208,7 @@ namespace CoordinateSharp /// Coordinate type /// Parent Coordinate object [Obsolete("Method is deprecated. You no longer need to pass a Coordinate object through the constructor.")] - public CoordinatePart(Double value, CoordinateType t, Coordinate c) - { + public CoordinatePart(Double value, CoordinateType t, Coordinate c) { this.parent = c; this.type = t; @@ -1287,8 +1246,7 @@ namespace CoordinateSharp /// Coordinate Part Position /// Parent Coordinate [Obsolete("Method is deprecated. You no longer need to pass a Coordinate object through the constructor.")] - public CoordinatePart(Int32 deg, Int32 min, Double sec, CoordinatesPosition pos, Coordinate c) - { + public CoordinatePart(Int32 deg, Int32 min, Double sec, CoordinatesPosition pos, Coordinate c) { this.parent = c; this.type = pos == CoordinatesPosition.N || pos == CoordinatesPosition.S ? CoordinateType.Lat : CoordinateType.Long; @@ -1308,12 +1266,12 @@ namespace CoordinateSharp minD += secD; //Decimal Minutes if (this.type == CoordinateType.Long) { - if (deg + (minD / 60) > 180) { throw new ArgumentOutOfRangeException("Degrees out of range", "Longitudinal Degrees cannot be greater than 180."); } + if (deg + minD / 60 > 180) { throw new ArgumentOutOfRangeException("Degrees out of range", "Longitudinal Degrees cannot be greater than 180."); } } else { - if (deg + (minD / 60) > 90) { throw new ArgumentOutOfRangeException("Degrees out of range", "Latitudinal Degrees cannot be greater than 90."); } + if (deg + minD / 60 > 90) { throw new ArgumentOutOfRangeException("Degrees out of range", "Latitudinal Degrees cannot be greater than 90."); } } this.decimalMinute = Convert.ToDouble(minD); - Decimal dd = Convert.ToDecimal(deg) + (minD / 60); + Decimal dd = Convert.ToDecimal(deg) + minD / 60; if (pos == CoordinatesPosition.S || pos == CoordinatesPosition.W) { @@ -1329,8 +1287,7 @@ namespace CoordinateSharp /// Coordinate Part Position /// Parent Coordinate object [Obsolete("Method is deprecated. You no longer need to pass a Coordinate object through the constructor.")] - public CoordinatePart(Int32 deg, Double minSec, CoordinatesPosition pos, Coordinate c) - { + public CoordinatePart(Int32 deg, Double minSec, CoordinatesPosition pos, Coordinate c) { this.parent = c; this.type = pos == CoordinatesPosition.N || pos == CoordinatesPosition.S ? CoordinateType.Lat : CoordinateType.Long; @@ -1341,9 +1298,9 @@ namespace CoordinateSharp if (minSec >= 60) { throw new ArgumentOutOfRangeException("Minutes out of range", "Minutes cannot be greater than or equal to 60."); } if (this.type == CoordinateType.Lat) { - if (deg + (minSec / 60) > 90) { throw new ArgumentOutOfRangeException("Degree out of range", "Latitudinal degrees cannot be greater than 90."); } + if (deg + minSec / 60 > 90) { throw new ArgumentOutOfRangeException("Degree out of range", "Latitudinal degrees cannot be greater than 90."); } } else { - if (deg + (minSec / 60) > 180) { throw new ArgumentOutOfRangeException("Degree out of range", "Longitudinal degrees cannot be greater than 180."); } + if (deg + minSec / 60 > 180) { throw new ArgumentOutOfRangeException("Degree out of range", "Longitudinal degrees cannot be greater than 180."); } } this.degrees = deg; this.decimalMinute = minSec; @@ -1356,7 +1313,7 @@ namespace CoordinateSharp sec *= 60; Decimal secD = Convert.ToDecimal(sec); this.seconds = Convert.ToDouble(secD); - Decimal dd = deg + (minD / 60); + Decimal dd = deg + minD / 60; if (pos == CoordinatesPosition.S || pos == CoordinatesPosition.W) { dd *= -1; @@ -1368,8 +1325,7 @@ namespace CoordinateSharp /// Creates an empty CoordinatePart. /// /// CoordinateType - public CoordinatePart(CoordinateType t) - { + public CoordinatePart(CoordinateType t) { this.type = t; this.decimalDegree = 0; this.degrees = 0; @@ -1382,8 +1338,7 @@ namespace CoordinateSharp /// /// Coordinate decimal value /// Coordinate type - public CoordinatePart(Double value, CoordinateType t) - { + public CoordinatePart(Double value, CoordinateType t) { this.type = t; if (this.type == CoordinateType.Long) { @@ -1418,8 +1373,7 @@ namespace CoordinateSharp /// Minutes /// Seconds /// Coordinate Part Position - public CoordinatePart(Int32 deg, Int32 min, Double sec, CoordinatesPosition pos) - { + public CoordinatePart(Int32 deg, Int32 min, Double sec, CoordinatesPosition pos) { this.type = pos == CoordinatesPosition.N || pos == CoordinatesPosition.S ? CoordinateType.Lat : CoordinateType.Long; if (deg < 0) { throw new ArgumentOutOfRangeException("Degrees out of range", "Degrees cannot be less than 0."); } @@ -1438,12 +1392,12 @@ namespace CoordinateSharp minD += secD; //Decimal Minutes if (this.type == CoordinateType.Long) { - if (deg + (minD / 60) > 180) { throw new ArgumentOutOfRangeException("Degrees out of range", "Longitudinal Degrees cannot be greater than 180."); } + if (deg + minD / 60 > 180) { throw new ArgumentOutOfRangeException("Degrees out of range", "Longitudinal Degrees cannot be greater than 180."); } } else { - if (deg + (minD / 60) > 90) { throw new ArgumentOutOfRangeException("Degrees out of range", "Latitudinal Degrees cannot be greater than 90."); } + if (deg + minD / 60 > 90) { throw new ArgumentOutOfRangeException("Degrees out of range", "Latitudinal Degrees cannot be greater than 90."); } } this.decimalMinute = Convert.ToDouble(minD); - Decimal dd = Convert.ToDecimal(deg) + (minD / 60); + Decimal dd = Convert.ToDecimal(deg) + minD / 60; if (pos == CoordinatesPosition.S || pos == CoordinatesPosition.W) { @@ -1457,8 +1411,7 @@ namespace CoordinateSharp /// Degrees /// Decimal Minutes /// Coordinate Part Position - public CoordinatePart(Int32 deg, Double minSec, CoordinatesPosition pos) - { + public CoordinatePart(Int32 deg, Double minSec, CoordinatesPosition pos) { this.type = pos == CoordinatesPosition.N || pos == CoordinatesPosition.S ? CoordinateType.Lat : CoordinateType.Long; if (deg < 0) { throw new ArgumentOutOfRangeException("Degree out of range", "Degree cannot be less than 0."); } @@ -1467,9 +1420,9 @@ namespace CoordinateSharp if (minSec >= 60) { throw new ArgumentOutOfRangeException("Minutes out of range", "Minutes cannot be greater than or equal to 60."); } if (this.type == CoordinateType.Lat) { - if (deg + (minSec / 60) > 90) { throw new ArgumentOutOfRangeException("Degree out of range", "Latitudinal degrees cannot be greater than 90."); } + if (deg + minSec / 60 > 90) { throw new ArgumentOutOfRangeException("Degree out of range", "Latitudinal degrees cannot be greater than 90."); } } else { - if (deg + (minSec / 60) > 180) { throw new ArgumentOutOfRangeException("Degree out of range", "Longitudinal degrees cannot be greater than 180."); } + if (deg + minSec / 60 > 180) { throw new ArgumentOutOfRangeException("Degree out of range", "Longitudinal degrees cannot be greater than 180."); } } this.degrees = deg; this.decimalMinute = minSec; @@ -1482,7 +1435,7 @@ namespace CoordinateSharp sec *= 60; Decimal secD = Convert.ToDecimal(sec); this.seconds = Convert.ToDouble(secD); - Decimal dd = deg + (minD / 60); + Decimal dd = deg + minD / 60; if (pos == CoordinatesPosition.S || pos == CoordinatesPosition.W) { dd *= -1; @@ -1513,46 +1466,25 @@ namespace CoordinateSharp /// /// CoordinateFormatOptions /// Formatted coordinate part string - private String FormatString(CoordinateFormatOptions options) - { - ToStringType type = ToStringType.Degree_Minute_Second; - Int32? rounding = null; - Boolean lead = false; - Boolean trail = false; - Boolean hyphen = false; - Boolean symbols = true; - Boolean degreeSymbol = true; - Boolean minuteSymbol = true; - Boolean secondsSymbol = true; - Boolean positionFirst = true; - + private String FormatString(CoordinateFormatOptions options) { #region Assign Formatting Rules - switch (options.Format) { - case CoordinateFormatType.Degree_Minutes_Seconds: - type = ToStringType.Degree_Minute_Second; - break; - case CoordinateFormatType.Degree_Decimal_Minutes: - type = ToStringType.Degree_Decimal_Minute; - break; - case CoordinateFormatType.Decimal_Degree: - type = ToStringType.Decimal_Degree; - break; - case CoordinateFormatType.Decimal: - type = ToStringType.Decimal; - break; - default: - type = ToStringType.Degree_Minute_Second; - break; - } - rounding = options.Round; - lead = options.Display_Leading_Zeros; - trail = options.Display_Trailing_Zeros; - symbols = options.Display_Symbols; - degreeSymbol = options.Display_Degree_Symbol; - minuteSymbol = options.Display_Minute_Symbol; - secondsSymbol = options.Display_Seconds_Symbol; - hyphen = options.Display_Hyphens; - positionFirst = options.Position_First; + ToStringType type = options.Format switch + { + CoordinateFormatType.Degree_Minutes_Seconds => ToStringType.Degree_Minute_Second, + CoordinateFormatType.Degree_Decimal_Minutes => ToStringType.Degree_Decimal_Minute, + CoordinateFormatType.Decimal_Degree => ToStringType.Decimal_Degree, + CoordinateFormatType.Decimal => ToStringType.Decimal, + _ => ToStringType.Degree_Minute_Second, + }; + Int32? rounding = options.Round; + Boolean lead = options.Display_Leading_Zeros; + Boolean trail = options.Display_Trailing_Zeros; + Boolean symbols = options.Display_Symbols; + Boolean degreeSymbol = options.Display_Degree_Symbol; + Boolean minuteSymbol = options.Display_Minute_Symbol; + Boolean secondsSymbol = options.Display_Seconds_Symbol; + Boolean hyphen = options.Display_Hyphens; + Boolean positionFirst = options.Position_First; #endregion switch (type) { @@ -1576,8 +1508,7 @@ namespace CoordinateSharp return String.Empty; } //DMS Coordinate Format - private String ToDegreeMinuteSecondString(Int32 rounding, Boolean lead, Boolean trail, Boolean symbols, Boolean degreeSymbol, Boolean minuteSymbol, Boolean secondSymbol, Boolean hyphen, Boolean positionFirst) - { + private String ToDegreeMinuteSecondString(Int32 rounding, Boolean lead, Boolean trail, Boolean symbols, Boolean degreeSymbol, Boolean minuteSymbol, Boolean secondSymbol, Boolean hyphen, Boolean positionFirst) { String leadString = this.Leading_Trailing_Format(lead, false, rounding, this.Position); String d = String.Format(leadString, this.Degrees); // Degree String @@ -1602,8 +1533,7 @@ namespace CoordinateSharp : d + ds + hs + minute + ms + hs + second + ss + hs + this.Position.ToString(); } //DDM Coordinate Format - private String ToDegreeDecimalMinuteString(Int32 rounding, Boolean lead, Boolean trail, Boolean symbols, Boolean degreeSymbol, Boolean minuteSymbol, Boolean hyphen, Boolean positionFirst) - { + private String ToDegreeDecimalMinuteString(Int32 rounding, Boolean lead, Boolean trail, Boolean symbols, Boolean degreeSymbol, Boolean minuteSymbol, Boolean hyphen, Boolean positionFirst) { String leadString = "{0:0"; if (lead) { if (this.Position == CoordinatesPosition.E || this.Position == CoordinatesPosition.W) { @@ -1646,8 +1576,7 @@ namespace CoordinateSharp } ////DD Coordinate Format - private String ToDecimalDegreeString(Int32 rounding, Boolean lead, Boolean trail, Boolean symbols, Boolean degreeSymbol, Boolean positionFirst, Boolean hyphen) - { + private String ToDecimalDegreeString(Int32 rounding, Boolean lead, Boolean trail, Boolean symbols, Boolean degreeSymbol, Boolean positionFirst, Boolean hyphen) { String degreeS = ""; String hyph = " "; if (degreeSymbol) { degreeS = "º"; } @@ -1672,15 +1601,14 @@ namespace CoordinateSharp } leadTrail += "}"; - Double result = (this.Degrees) + (Convert.ToDouble(this.Minutes)) / 60 + (Convert.ToDouble(this.Seconds)) / 3600; + Double result = this.Degrees + Convert.ToDouble(this.Minutes) / 60 + Convert.ToDouble(this.Seconds) / 3600; result = Math.Round(result, rounding); String d = String.Format(leadTrail, Math.Abs(result)); return positionFirst ? this.Position.ToString() + hyph + d + degreeS : d + degreeS + hyph + this.Position.ToString(); } - private String Leading_Trailing_Format(Boolean isLead, Boolean isTrail, Int32 rounding, CoordinatesPosition? p = null) - { + private String Leading_Trailing_Format(Boolean isLead, Boolean isTrail, Int32 rounding, CoordinatesPosition? p = null) { String leadString = "{0:0"; if (isLead) { if (p != null) { @@ -1706,18 +1634,16 @@ namespace CoordinateSharp } - private String FormatError(String argument, String rule) => "'" + argument + "' is not a valid argument for string format rule: " + rule + "."; + //private String FormatError(String argument, String rule) => "'" + argument + "' is not a valid argument for string format rule: " + rule + "."; - private enum ToStringType - { + private enum ToStringType { Decimal_Degree, Degree_Decimal_Minute, Degree_Minute_Second, Decimal } /// /// Notify the correct properties and parent properties. /// /// Property Type - private void NotifyProperties(PropertyTypes p) - { + private void NotifyProperties(PropertyTypes p) { switch (p) { case PropertyTypes.DecimalDegree: this.NotifyPropertyChanged("DecimalDegree"); @@ -1786,8 +1712,7 @@ namespace CoordinateSharp /// /// Used for notifying the correct properties. /// - private enum PropertyTypes - { + private enum PropertyTypes { DecimalDegree, DecimalMinute, Position, Degree, Minute, Second, FormatChange } @@ -1811,12 +1736,7 @@ namespace CoordinateSharp /// } /// /// - public static Boolean TryParse(String s, out CoordinatePart cp) - { - cp = null; - - return FormatFinder_CoordPart.TryParse(s, out cp) ? true : false; - } + public static Boolean TryParse(String s, out CoordinatePart cp) => FormatFinder_CoordPart.TryParse(s, out cp) ? true : false; /// /// Attempts to parse a string into a CoordinatePart. /// @@ -1833,9 +1753,7 @@ namespace CoordinateSharp /// } /// /// - public static Boolean TryParse(String s, CoordinateType t, out CoordinatePart cp) - { - cp = null; + public static Boolean TryParse(String s, CoordinateType t, out CoordinatePart cp) { //Comma at beginning parses to long //Asterik forces lat s = t == CoordinateType.Long ? "," + s : "*" + s; diff --git a/CoordinateSharp/CoordinateSharp_Core.csproj b/CoordinateSharp/CoordinateSharp_Core.csproj new file mode 100644 index 0000000..186478d --- /dev/null +++ b/CoordinateSharp/CoordinateSharp_Core.csproj @@ -0,0 +1,12 @@ + + + + netcoreapp3.0 + CoordinateSharp + CoordinateSharp + A simple .NET standard library that is designed to assist with geographic coordinate conversions, formatting and location based celestial calculations. + Copyright 2019 + 1.1.5.2 + + + diff --git a/CoordinateSharp/Distance.cs b/CoordinateSharp/Distance.cs index 43434af..463acc3 100644 --- a/CoordinateSharp/Distance.cs +++ b/CoordinateSharp/Distance.cs @@ -1,442 +1,382 @@ using System; using System.Diagnostics; -namespace CoordinateSharp -{ + +namespace CoordinateSharp { + /// + /// Contains distance values between two coordinates. + /// + [Serializable] + public class Distance { + /// - /// Contains distance values between two coordinates. + /// Initializes a distance object using Haversine (Spherical Earth). /// - [Serializable] - public class Distance - { - private double kilometers; - private double miles; - private double feet; - private double meters; - private double bearing; - private double nauticalMiles; - - /// - /// Initializes a distance object using Haversine (Spherical Earth). - /// - /// Coordinate 1 - /// Coordinate 2 - public Distance(Coordinate c1, Coordinate c2) - { - Haversine(c1, c2); - } - /// - /// Initializes a distance object using Haversine (Spherical Earth) or Vincenty (Elliptical Earth). - /// - /// Coordinate 1 - /// Coordinate 2 - /// Shape of earth - public Distance(Coordinate c1, Coordinate c2, Shape shape) - { - if (shape == Shape.Sphere) - { - Haversine(c1, c2); - } - else - { - Vincenty(c1, c2); - } - } - /// - /// Initializes distance object based on distance in KM - /// - /// Kilometers - public Distance(double km) - { - kilometers = km; - meters = km * 1000; - feet = meters * 3.28084; - miles = meters * 0.000621371; - nauticalMiles = meters * 0.0005399565; - bearing = 0;//None specified - } - /// - /// Initializaes distance object based on specified distance and measurement type - /// - /// Distance - /// Measurement type - - public Distance(double distance, DistanceType type) - { - bearing = 0; - switch (type) - { - case DistanceType.Feet: - feet = distance; - meters = feet * 0.3048; - kilometers = meters / 1000; - miles = meters * 0.000621371; - nauticalMiles = meters * 0.0005399565; - break; - case DistanceType.Kilometers: - kilometers = distance; - meters = kilometers * 1000; - feet = meters * 3.28084; - miles = meters * 0.000621371; - nauticalMiles = meters * 0.0005399565; - break; - case DistanceType.Meters: - meters = distance; - kilometers = meters / 1000; - feet = meters * 3.28084; - miles = meters * 0.000621371; - nauticalMiles = meters * 0.0005399565; - break; - case DistanceType.Miles: - miles = distance; - meters = miles * 1609.344; - feet = meters * 3.28084; - kilometers = meters / 1000; - nauticalMiles = meters * 0.0005399565; - break; - case DistanceType.NauticalMiles: - nauticalMiles = distance; - meters = nauticalMiles * 1852.001; - feet = meters * 3.28084; - kilometers = meters / 1000; - miles = meters * 0.000621371; - break; - default: - kilometers = distance; - meters = distance * 1000; - feet = meters * 3.28084; - miles = meters * 0.000621371; - nauticalMiles = meters * 0.0005399565; - break; - } - } - private void Vincenty(Coordinate coord1, Coordinate coord2) - { - double lat1, lat2, lon1, lon2; - double d, crs12, crs21; - - - lat1 = coord1.Latitude.ToRadians(); - lat2 = coord2.Latitude.ToRadians(); - lon1 = coord1.Longitude.ToRadians() * -1; //REVERSE FOR CALC 2.1.1.1 - lon2 = coord2.Longitude.ToRadians() * -1; //REVERSE FOR CALC 2.1.1.1 - - //Ensure datums match between coords - if ((coord1.equatorial_radius != coord2.equatorial_radius) || (coord1.inverse_flattening != coord2.inverse_flattening)) - { - throw new InvalidOperationException("The datum set does not match between Coordinate objects."); - } - double[] ellipse = new double[] { coord1.equatorial_radius, coord1.inverse_flattening }; - - - // elliptic code - double[] cde = Distance_Assistant.Dist_Ell(lat1, -lon1, lat2, -lon2, ellipse); // ellipse uses East negative - crs12 = cde[1] * (180 / Math.PI); //Bearing - crs21 = cde[2] * (180 / Math.PI); //Reverse Bearing - d = cde[0]; //Distance - - bearing = crs12; - //reverseBearing = crs21; - meters = d; - kilometers = d / 1000; - feet = d * 3.28084; - miles = d * 0.000621371; - nauticalMiles = d * 0.0005399565; - - } - - private void Haversine(Coordinate coord1, Coordinate coord2) - { - ////RADIANS - double lat1 = coord1.Latitude.ToRadians(); - double long1 = coord1.Longitude.ToRadians(); - double lat2 = coord2.Latitude.ToRadians(); - double long2 = coord2.Longitude.ToRadians(); - - //Distance Calcs - double R = 6371000; //6378137.0;//6371e3; //meters - double latRad = coord2.Latitude.ToRadians() - coord1.Latitude.ToRadians(); - double longRad = coord2.Longitude.ToRadians() - coord1.Longitude.ToRadians(); - - double a = Math.Sin(latRad / 2.0) * Math.Sin(latRad / 2.0) + - Math.Cos(lat1) * Math.Cos(lat2) * Math.Sin(longRad / 2.0) * Math.Sin(longRad / 2.0); - double cl = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a)); - double dist = R * cl; - - //Get bearing - double dLong = long2 - long1; - double y = Math.Sin(dLong) * Math.Cos(lat2); - double x = Math.Cos(lat1) * Math.Sin(lat2) - Math.Sin(lat1) * Math.Cos(lat2) * Math.Cos(dLong); - double brng = Math.Atan2(y, x) * (180 / Math.PI); //Convert bearing back to degrees. - - //if (brng < 0) { brng -= 180; brng = Math.Abs(brng); } - brng = (brng + 360) % 360; //v2.1.1.1 NORMALIZE HEADING - - kilometers = dist / 1000; - meters = dist; - feet = dist * 3.28084; - miles = dist * 0.000621371; - nauticalMiles = dist * 0.0005399565; - bearing = brng; - } - /// - /// Distance in Kilometers - /// - public double Kilometers - { - get { return kilometers; } - } - /// - /// Distance in Statute Miles - /// - public double Miles - { - get { return miles; } - } - /// - /// Distance in Nautical Miles - /// - public double NauticalMiles - { - get { return nauticalMiles; } - } - /// - /// Distance in Meters - /// - public double Meters - { - get { return meters; } - } - /// - /// Distance in Feet - /// - public double Feet - { - get { return feet; } - } - /// - /// Initial Bearing from Coordinate 1 to Coordinate 2 - /// - public double Bearing - { - get { return bearing; } - } + /// Coordinate 1 + /// Coordinate 2 + public Distance(Coordinate c1, Coordinate c2) => this.Haversine(c1, c2); + /// + /// Initializes a distance object using Haversine (Spherical Earth) or Vincenty (Elliptical Earth). + /// + /// Coordinate 1 + /// Coordinate 2 + /// Shape of earth + public Distance(Coordinate c1, Coordinate c2, Shape shape) { + if (shape == Shape.Sphere) { + this.Haversine(c1, c2); + } else { + this.Vincenty(c1, c2); + } } /// - /// Distance measurement type + /// Initializes distance object based on distance in KM /// - public enum DistanceType - { - /// - /// Distance in Meters - /// - Meters, - /// - /// Distance in Kilometers - /// - Kilometers, - /// - /// Distance in Feet - /// - Feet, - /// - /// Distance in Statute Miles - /// - Miles, - /// - /// Distance in Nautical Miles - /// - NauticalMiles + /// Kilometers + public Distance(Double km) { + this.Kilometers = km; + this.Meters = km * 1000; + this.Feet = this.Meters * 3.28084; + this.Miles = this.Meters * 0.000621371; + this.NauticalMiles = this.Meters * 0.0005399565; + this.Bearing = 0;//None specified + } + /// + /// Initializaes distance object based on specified distance and measurement type + /// + /// Distance + /// Measurement type + + public Distance(Double distance, DistanceType type) { + this.Bearing = 0; + switch (type) { + case DistanceType.Feet: + this.Feet = distance; + this.Meters = this.Feet * 0.3048; + this.Kilometers = this.Meters / 1000; + this.Miles = this.Meters * 0.000621371; + this.NauticalMiles = this.Meters * 0.0005399565; + break; + case DistanceType.Kilometers: + this.Kilometers = distance; + this.Meters = this.Kilometers * 1000; + this.Feet = this.Meters * 3.28084; + this.Miles = this.Meters * 0.000621371; + this.NauticalMiles = this.Meters * 0.0005399565; + break; + case DistanceType.Meters: + this.Meters = distance; + this.Kilometers = this.Meters / 1000; + this.Feet = this.Meters * 3.28084; + this.Miles = this.Meters * 0.000621371; + this.NauticalMiles = this.Meters * 0.0005399565; + break; + case DistanceType.Miles: + this.Miles = distance; + this.Meters = this.Miles * 1609.344; + this.Feet = this.Meters * 3.28084; + this.Kilometers = this.Meters / 1000; + this.NauticalMiles = this.Meters * 0.0005399565; + break; + case DistanceType.NauticalMiles: + this.NauticalMiles = distance; + this.Meters = this.NauticalMiles * 1852.001; + this.Feet = this.Meters * 3.28084; + this.Kilometers = this.Meters / 1000; + this.Miles = this.Meters * 0.000621371; + break; + default: + this.Kilometers = distance; + this.Meters = distance * 1000; + this.Feet = this.Meters * 3.28084; + this.Miles = this.Meters * 0.000621371; + this.NauticalMiles = this.Meters * 0.0005399565; + break; + } + } + private void Vincenty(Coordinate coord1, Coordinate coord2) { + Double lat1, lat2, lon1, lon2; + Double d, crs12; + + + lat1 = coord1.Latitude.ToRadians(); + lat2 = coord2.Latitude.ToRadians(); + lon1 = coord1.Longitude.ToRadians() * -1; //REVERSE FOR CALC 2.1.1.1 + lon2 = coord2.Longitude.ToRadians() * -1; //REVERSE FOR CALC 2.1.1.1 + + //Ensure datums match between coords + if (coord1.equatorial_radius != coord2.equatorial_radius || coord1.inverse_flattening != coord2.inverse_flattening) { + throw new InvalidOperationException("The datum set does not match between Coordinate objects."); + } + Double[] ellipse = new Double[] { coord1.equatorial_radius, coord1.inverse_flattening }; + + + // elliptic code + Double[] cde = Distance_Assistant.Dist_Ell(lat1, -lon1, lat2, -lon2, ellipse); // ellipse uses East negative + crs12 = cde[1] * (180 / Math.PI); //Bearing + _ = cde[2] * (180 / Math.PI); //Reverse Bearing + d = cde[0]; //Distance + + this.Bearing = crs12; + //reverseBearing = crs21; + this.Meters = d; + this.Kilometers = d / 1000; + this.Feet = d * 3.28084; + this.Miles = d * 0.000621371; + this.NauticalMiles = d * 0.0005399565; + } - [Serializable] - internal class Distance_Assistant - { - /// - /// Returns new geodetic coordinate in radians - /// - /// Latitude in Radians - /// Longitude in Radians - /// Bearing - /// Distance - /// Earth Ellipse Values - /// double[] - public static double[] Direct_Ell(double glat1, double glon1, double faz, double s, double[] ellipse) - { - glon1 *= -1; //REVERSE LONG FOR CALC 2.1.1.1 - double EPS = 0.00000000005;//Used to determine if starting at pole. - double r, tu, sf, cf, b, cu, su, sa, c2a, x, c, d, y, sy = 0, cy = 0, cz = 0, e = 0; - double glat2, glon2, f; + private void Haversine(Coordinate coord1, Coordinate coord2) { + ////RADIANS + Double lat1 = coord1.Latitude.ToRadians(); + Double long1 = coord1.Longitude.ToRadians(); + Double lat2 = coord2.Latitude.ToRadians(); + Double long2 = coord2.Longitude.ToRadians(); - //Determine if near pole - if ((Math.Abs(Math.Cos(glat1)) < EPS) && !(Math.Abs(Math.Sin(faz)) < EPS)) - { - Debug.WriteLine("Warning: Location is at earth's pole. Only N-S courses are meaningful at this location."); - } + //Distance Calcs + Double R = 6371000; //6378137.0;//6371e3; //meters + Double latRad = coord2.Latitude.ToRadians() - coord1.Latitude.ToRadians(); + Double longRad = coord2.Longitude.ToRadians() - coord1.Longitude.ToRadians(); + Double a = Math.Sin(latRad / 2.0) * Math.Sin(latRad / 2.0) + + Math.Cos(lat1) * Math.Cos(lat2) * Math.Sin(longRad / 2.0) * Math.Sin(longRad / 2.0); + Double cl = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a)); + Double dist = R * cl; - double a = ellipse[0];//Equitorial Radius - f = 1 / ellipse[1];//Flattening - r = 1 - f; - tu = r * Math.Tan(glat1); - sf = Math.Sin(faz); - cf = Math.Cos(faz); - if (cf == 0) - { - b = 0.0; - } - else - { - b = 2.0 * Math.Atan2(tu, cf); - } - cu = 1.0 / Math.Sqrt(1 + tu * tu); - su = tu * cu; - sa = cu * sf; - c2a = 1 - sa * sa; - x = 1.0 + Math.Sqrt(1.0 + c2a * (1.0 / (r * r) - 1.0)); - x = (x - 2.0) / x; - c = 1.0 - x; - c = (x * x / 4.0 + 1.0) / c; - d = (0.375 * x * x - 1.0) * x; - tu = s / (r * a * c); - y = tu; - c = y + 1; - while (Math.Abs(y - c) > EPS) - { - sy = Math.Sin(y); - cy = Math.Cos(y); - cz = Math.Cos(b + y); - e = 2.0 * cz * cz - 1.0; - c = y; - x = e * cy; - y = e + e - 1.0; - y = (((sy * sy * 4.0 - 3.0) * y * cz * d / 6.0 + x) * - d / 4.0 - cz) * sy * d + tu; - } + //Get bearing + Double dLong = long2 - long1; + Double y = Math.Sin(dLong) * Math.Cos(lat2); + Double x = Math.Cos(lat1) * Math.Sin(lat2) - Math.Sin(lat1) * Math.Cos(lat2) * Math.Cos(dLong); + Double brng = Math.Atan2(y, x) * (180 / Math.PI); //Convert bearing back to degrees. - b = cu * cy * cf - su * sy; - c = r * Math.Sqrt(sa * sa + b * b); - d = su * cy + cu * sy * cf; + //if (brng < 0) { brng -= 180; brng = Math.Abs(brng); } + brng = (brng + 360) % 360; //v2.1.1.1 NORMALIZE HEADING - glat2 = ModM.ModLat(Math.Atan2(d, c)); - c = cu * cy - su * sy * cf; - x = Math.Atan2(sy * sf, c); - c = ((-3.0 * c2a + 4.0) * f + 4.0) * c2a * f / 16.0; - d = ((e * cy * c + cz) * sy * c + y) * sa; - glon2 = ModM.ModLon(glon1 + x - (1.0 - c) * d * f); //Adjust for IDL - //baz = ModM.ModCrs(Math.Atan2(sa, b) + Math.PI); - return new double[] { glat2, glon2 }; - } - /// - /// Returns new geodetic coordinate in radians - /// - /// Latitude in radians - /// Longitude in radians - /// Bearing - /// Distance - /// double[] - public static double[] Direct(double lat1, double lon1, double crs12, double d12) - { - lon1 *= -1; //REVERSE LONG FOR CALC 2.1.1.1 - var EPS = 0.00000000005;//Used to determine if near pole. - double dlon, lat, lon; - d12 = d12 * 0.0005399565; //convert meter to nm - d12 = d12 / (180 * 60 / Math.PI);//Convert to Radian - //Determine if near pole - if ((Math.Abs(Math.Cos(lat1)) < EPS) && !(Math.Abs(Math.Sin(crs12)) < EPS)) - { - Debug.WriteLine("Warning: Location is at earth's pole. Only N-S courses are meaningful at this location."); - } - - lat = Math.Asin(Math.Sin(lat1) * Math.Cos(d12) + - Math.Cos(lat1) * Math.Sin(d12) * Math.Cos(crs12)); - if (Math.Abs(Math.Cos(lat)) < EPS) - { - lon = 0.0; //endpoint a pole - } - else - { - dlon = Math.Atan2(Math.Sin(crs12) * Math.Sin(d12) * Math.Cos(lat1), - Math.Cos(d12) - Math.Sin(lat1) * Math.Sin(lat)); - lon = ModM.Mod(lon1 - dlon + Math.PI, 2 * Math.PI) - Math.PI; - } - - return new double[] { lat, lon }; - } - public static double[] Dist_Ell(double glat1, double glon1, double glat2, double glon2, double[] ellipse) - { - double a = ellipse[0]; //Equitorial Radius - double f = 1 / ellipse[1]; //Flattening - - double r, tu1, tu2, cu1, su1, cu2, s1, b1, f1; - double x = 0, sx = 0, cx = 0, sy = 0, cy = 0, y = 0, sa = 0, c2a = 0, cz = 0, e = 0, c = 0, d = 0; - double EPS = 0.00000000005; - double faz, baz, s; - double iter = 1; - double MAXITER = 100; - if ((glat1 + glat2 == 0.0) && (Math.Abs(glon1 - glon2) == Math.PI)) - { - Debug.WriteLine("Warning: Course and distance between antipodal points is undefined"); - glat1 = glat1 + 0.00001; // allow algorithm to complete - } - if (glat1 == glat2 && (glon1 == glon2 || Math.Abs(Math.Abs(glon1 - glon2) - 2 * Math.PI) < EPS)) - { - Debug.WriteLine("Warning: Points 1 and 2 are identical- course undefined"); - //D - //crs12 - //crs21 - return new double[] { 0, 0, Math.PI }; - } - r = 1 - f; - tu1 = r * Math.Tan(glat1); - tu2 = r * Math.Tan(glat2); - cu1 = 1.0 / Math.Sqrt(1.0 + tu1 * tu1); - su1 = cu1 * tu1; - cu2 = 1.0 / Math.Sqrt(1.0 + tu2 * tu2); - s1 = cu1 * cu2; - b1 = s1 * tu2; - f1 = b1 * tu1; - x = glon2 - glon1; - d = x + 1; // force one pass - while ((Math.Abs(d - x) > EPS) && (iter < MAXITER)) - { - iter = iter + 1; - sx = Math.Sin(x); - cx = Math.Cos(x); - tu1 = cu2 * sx; - tu2 = b1 - su1 * cu2 * cx; - sy = Math.Sqrt(tu1 * tu1 + tu2 * tu2); - cy = s1 * cx + f1; - y = Math.Atan2(sy, cy); - sa = s1 * sx / sy; - c2a = 1 - sa * sa; - cz = f1 + f1; - if (c2a > 0.0) - { - cz = cy - cz / c2a; - } - e = cz * cz * 2.0 - 1.0; - c = ((-3.0 * c2a + 4.0) * f + 4.0) * c2a * f / 16.0; - d = x; - x = ((e * cy * c + cz) * sy * c + y) * sa; - x = (1.0 - c) * x * f + glon2 - glon1; - } - faz = ModM.ModCrs(Math.Atan2(tu1, tu2)); - baz = ModM.ModCrs(Math.Atan2(cu1 * sx, b1 * cx - su1 * cu2) + Math.PI); - x = Math.Sqrt((1 / (r * r) - 1) * c2a + 1); - x += 1; - x = (x - 2.0) / x; - c = 1.0 - x; - c = (x * x / 4.0 + 1.0) / c; - d = (0.375 * x * x - 1.0) * x; - x = e * cy; - s = ((((sy * sy * 4.0 - 3.0) * (1.0 - e - e) * cz * d / 6.0 - x) * d / 4.0 + cz) * sy * d + y) * c * a * r; - - if (Math.Abs(iter - MAXITER) < EPS) - { - Debug.WriteLine("Warning: Distance algorithm did not converge"); - } - - return new double[] { s, faz, baz }; - } + this.Kilometers = dist / 1000; + this.Meters = dist; + this.Feet = dist * 3.28084; + this.Miles = dist * 0.000621371; + this.NauticalMiles = dist * 0.0005399565; + this.Bearing = brng; } + /// + /// Distance in Kilometers + /// + public Double Kilometers { get; private set; } + /// + /// Distance in Statute Miles + /// + public Double Miles { get; private set; } + /// + /// Distance in Nautical Miles + /// + public Double NauticalMiles { get; private set; } + /// + /// Distance in Meters + /// + public Double Meters { get; private set; } + /// + /// Distance in Feet + /// + public Double Feet { get; private set; } + /// + /// Initial Bearing from Coordinate 1 to Coordinate 2 + /// + public Double Bearing { get; private set; } + } + /// + /// Distance measurement type + /// + public enum DistanceType { + /// + /// Distance in Meters + /// + Meters, + /// + /// Distance in Kilometers + /// + Kilometers, + /// + /// Distance in Feet + /// + Feet, + /// + /// Distance in Statute Miles + /// + Miles, + /// + /// Distance in Nautical Miles + /// + NauticalMiles + } + + [Serializable] + internal class Distance_Assistant { + /// + /// Returns new geodetic coordinate in radians + /// + /// Latitude in Radians + /// Longitude in Radians + /// Bearing + /// Distance + /// Earth Ellipse Values + /// double[] + public static Double[] Direct_Ell(Double glat1, Double glon1, Double faz, Double s, Double[] ellipse) { + glon1 *= -1; //REVERSE LONG FOR CALC 2.1.1.1 + Double EPS = 0.00000000005;//Used to determine if starting at pole. + Double r, tu, sf, cf, b, cu, su, sa, c2a, x, c, d, y, sy = 0, cy = 0, cz = 0, e = 0; + Double glat2, glon2, f; + + //Determine if near pole + if (Math.Abs(Math.Cos(glat1)) < EPS && !(Math.Abs(Math.Sin(faz)) < EPS)) { + Debug.WriteLine("Warning: Location is at earth's pole. Only N-S courses are meaningful at this location."); + } + + + Double a = ellipse[0];//Equitorial Radius + f = 1 / ellipse[1];//Flattening + r = 1 - f; + tu = r * Math.Tan(glat1); + sf = Math.Sin(faz); + cf = Math.Cos(faz); + b = cf == 0 ? 0.0 : 2.0 * Math.Atan2(tu, cf); + cu = 1.0 / Math.Sqrt(1 + tu * tu); + su = tu * cu; + sa = cu * sf; + c2a = 1 - sa * sa; + x = 1.0 + Math.Sqrt(1.0 + c2a * (1.0 / (r * r) - 1.0)); + x = (x - 2.0) / x; + c = 1.0 - x; + c = (x * x / 4.0 + 1.0) / c; + d = (0.375 * x * x - 1.0) * x; + tu = s / (r * a * c); + y = tu; + c = y + 1; + while (Math.Abs(y - c) > EPS) { + sy = Math.Sin(y); + cy = Math.Cos(y); + cz = Math.Cos(b + y); + e = 2.0 * cz * cz - 1.0; + c = y; + x = e * cy; + y = e + e - 1.0; + y = (((sy * sy * 4.0 - 3.0) * y * cz * d / 6.0 + x) * + d / 4.0 - cz) * sy * d + tu; + } + + b = cu * cy * cf - su * sy; + c = r * Math.Sqrt(sa * sa + b * b); + d = su * cy + cu * sy * cf; + + glat2 = ModM.ModLat(Math.Atan2(d, c)); + c = cu * cy - su * sy * cf; + x = Math.Atan2(sy * sf, c); + c = ((-3.0 * c2a + 4.0) * f + 4.0) * c2a * f / 16.0; + d = ((e * cy * c + cz) * sy * c + y) * sa; + glon2 = ModM.ModLon(glon1 + x - (1.0 - c) * d * f); //Adjust for IDL + //baz = ModM.ModCrs(Math.Atan2(sa, b) + Math.PI); + return new Double[] { glat2, glon2 }; + } + /// + /// Returns new geodetic coordinate in radians + /// + /// Latitude in radians + /// Longitude in radians + /// Bearing + /// Distance + /// double[] + public static Double[] Direct(Double lat1, Double lon1, Double crs12, Double d12) { + lon1 *= -1; //REVERSE LONG FOR CALC 2.1.1.1 + Double EPS = 0.00000000005;//Used to determine if near pole. + Double dlon, lat, lon; + d12 *= 0.0005399565; //convert meter to nm + d12 /= 180 * 60 / Math.PI;//Convert to Radian + //Determine if near pole + if (Math.Abs(Math.Cos(lat1)) < EPS && !(Math.Abs(Math.Sin(crs12)) < EPS)) { + Debug.WriteLine("Warning: Location is at earth's pole. Only N-S courses are meaningful at this location."); + } + + lat = Math.Asin(Math.Sin(lat1) * Math.Cos(d12) + + Math.Cos(lat1) * Math.Sin(d12) * Math.Cos(crs12)); + if (Math.Abs(Math.Cos(lat)) < EPS) { + lon = 0.0; //endpoint a pole + } else { + dlon = Math.Atan2(Math.Sin(crs12) * Math.Sin(d12) * Math.Cos(lat1), + Math.Cos(d12) - Math.Sin(lat1) * Math.Sin(lat)); + lon = ModM.Mod(lon1 - dlon + Math.PI, 2 * Math.PI) - Math.PI; + } + + return new Double[] { lat, lon }; + } + public static Double[] Dist_Ell(Double glat1, Double glon1, Double glat2, Double glon2, Double[] ellipse) { + Double a = ellipse[0]; //Equitorial Radius + Double f = 1 / ellipse[1]; //Flattening + + Double r, tu1, tu2, cu1, su1, cu2, s1, b1, f1; + Double sx = 0, cx = 0, sy = 0, cy = 0, y = 0, c2a = 0, cz = 0, e = 0; + Double EPS = 0.00000000005; + Double faz, baz, s; + Double iter = 1; + Double MAXITER = 100; + if (glat1 + glat2 == 0.0 && Math.Abs(glon1 - glon2) == Math.PI) { + Debug.WriteLine("Warning: Course and distance between antipodal points is undefined"); + glat1 += 0.00001; // allow algorithm to complete + } + if (glat1 == glat2 && (glon1 == glon2 || Math.Abs(Math.Abs(glon1 - glon2) - 2 * Math.PI) < EPS)) { + Debug.WriteLine("Warning: Points 1 and 2 are identical- course undefined"); + //D + //crs12 + //crs21 + return new Double[] { 0, 0, Math.PI }; + } + r = 1 - f; + tu1 = r * Math.Tan(glat1); + tu2 = r * Math.Tan(glat2); + cu1 = 1.0 / Math.Sqrt(1.0 + tu1 * tu1); + su1 = cu1 * tu1; + cu2 = 1.0 / Math.Sqrt(1.0 + tu2 * tu2); + s1 = cu1 * cu2; + b1 = s1 * tu2; + f1 = b1 * tu1; + Double x = glon2 - glon1; + Double d = x + 1; + Double c; + while (Math.Abs(d - x) > EPS && iter < MAXITER) { + iter += 1; + sx = Math.Sin(x); + cx = Math.Cos(x); + tu1 = cu2 * sx; + tu2 = b1 - su1 * cu2 * cx; + sy = Math.Sqrt(tu1 * tu1 + tu2 * tu2); + cy = s1 * cx + f1; + y = Math.Atan2(sy, cy); + Double sa = s1 * sx / sy; + c2a = 1 - sa * sa; + cz = f1 + f1; + if (c2a > 0.0) { + cz = cy - cz / c2a; + } + e = cz * cz * 2.0 - 1.0; + c = ((-3.0 * c2a + 4.0) * f + 4.0) * c2a * f / 16.0; + d = x; + x = ((e * cy * c + cz) * sy * c + y) * sa; + x = (1.0 - c) * x * f + glon2 - glon1; + } + faz = ModM.ModCrs(Math.Atan2(tu1, tu2)); + baz = ModM.ModCrs(Math.Atan2(cu1 * sx, b1 * cx - su1 * cu2) + Math.PI); + x = Math.Sqrt((1 / (r * r) - 1) * c2a + 1); + x += 1; + x = (x - 2.0) / x; + c = 1.0 - x; + c = (x * x / 4.0 + 1.0) / c; + d = (0.375 * x * x - 1.0) * x; + x = e * cy; + s = ((((sy * sy * 4.0 - 3.0) * (1.0 - e - e) * cz * d / 6.0 - x) * d / 4.0 + cz) * sy * d + y) * c * a * r; + + if (Math.Abs(iter - MAXITER) < EPS) { + Debug.WriteLine("Warning: Distance algorithm did not converge"); + } + + return new Double[] { s, faz, baz }; + } + } } diff --git a/CoordinateSharp/Eclipse/LunarData.cs b/CoordinateSharp/Eclipse/LunarData.cs index 1630fdd..20356f7 100644 --- a/CoordinateSharp/Eclipse/LunarData.cs +++ b/CoordinateSharp/Eclipse/LunarData.cs @@ -2,11 +2,9 @@ using System.Collections.Generic; using System.Linq; -namespace CoordinateSharp.Eclipse -{ - internal class LunarData - { - static double[] LE1601 = new double[] { +namespace CoordinateSharp.Eclipse { + internal class LunarData { + static readonly Double[] LE1601 = new Double[] { // 1601 1 18 2305831.105839, 15.0, 117.3, 0.033, -0.978, 3, -1.13536, 0.98335, 0.26794, @@ -1501,8 +1499,8 @@ namespace CoordinateSharp.Eclipse -3.09161, -1.92390, -0.82385, 0.02344, 0.87082, 1.97089, 3.13800, 337.8589683, 0.48164, -1.510e-04, -9.4195702, 0.13945, 3.390e-04 }; - static double[] LE1701 = new double[] - { + static readonly Double[] LE1701 = new Double[] + { // 1701 2 22 2342390.479120, 23.0, 8.2, 1.428, 0.463, 2, -14.82816, 1.02374, 0.27894, @@ -3039,9 +3037,9 @@ namespace CoordinateSharp.Eclipse -2.69377, -1.12653, 0.00000, -0.22214, 0.00000, 0.67926, 2.24840, 9.1688605, 0.46956, -7.000e-05, 3.0790301, 0.25315, -2.000e-04 - }; - static double[] LE1801 = new double[] - { + }; + static readonly Double[] LE1801 = new Double[] + { // 1801 3 30 2378949.725057, 5.0, 13.0, 2.857, 1.840, 1, -6.52639, 0.96247, 0.26225, @@ -4536,9 +4534,9 @@ namespace CoordinateSharp.Eclipse -1.50705, 0.00000, 0.00000, 0.44149, 0.00000, 0.00000, 2.38825, 72.2570493, 0.64062, -1.660e-04, 21.3680795, 0.01873, -1.222e-03 - }; - static double[] LE1901 = new double[] - { + }; + static readonly Double[] LE1901 = new Double[] + { // 1901 5 3 2415508.271269, 19.0, -0.9, 1.043, -0.033, 3, -14.26838, 0.90110, 0.24553, @@ -5913,9 +5911,9 @@ namespace CoordinateSharp.Eclipse -3.17609, -2.02249, -0.94259, -0.05589, 0.83086, 1.91081, 3.06398, 296.2552752, 0.52835, -1.150e-04, -21.2212594, 0.04170, 8.250e-04 - }; - static double[] LE2001 = new double[] - { + }; + static readonly Double[] LE2001 = new Double[] + { // 2001 1 9 2451919.348374, 20.0, 64.1, 2.162, 1.189, 1, 3.29475, 1.02253, 0.27861, @@ -7284,9 +7282,9 @@ namespace CoordinateSharp.Eclipse -2.37000, 0.00000, 0.00000, -0.25042, 0.00000, 0.00000, 1.86680, 329.8184696, 0.49978, -6.760e-04, -13.3626205, 0.22960, 3.990e-04 - }; - static double[] LE2101 = new double[] - { + }; + static readonly Double[] LE2101 = new Double[] + { // 2101 2 14 2488478.618055, 3.0, 205.1, 2.218, 1.183, 1, 12.59941, 0.95660, 0.26065, @@ -8715,9 +8713,9 @@ namespace CoordinateSharp.Eclipse -2.67458, -1.48697, 0.00000, -0.25460, 0.00000, 0.97583, 2.16508, 28.0294400, 0.59158, 9.000e-05, 10.7019400, 0.15900, -6.160e-04 - }; - static double[] LE2201 = new double[] - { + }; + static readonly Double[] LE2201 = new Double[] + { // 2201 3 20 2525037.204912, 17.0, 444.3, 0.532, -0.560, 3, 4.85759, 0.90005, 0.24524, @@ -10230,9 +10228,9 @@ namespace CoordinateSharp.Eclipse -2.66449, -1.75055, -0.80821, 0.00923, 0.82677, 1.76918, 2.68236, 63.0500500, 0.65278, 8.590e-04, 21.0508309, 0.17085, -1.218e-03 - }; - static double[] LE2301 = new double[] - { + }; + static readonly Double[] LE2301 = new Double[] + { // 2301 5 23 2561625.186996, 16.0, 720.0, 1.809, 0.754, 2, 8.04603, 0.91666, 0.24977, @@ -11751,8 +11749,8 @@ namespace CoordinateSharp.Eclipse -2.07354, -0.65576, 0.00000, 0.44265, 0.00000, 1.54366, 2.95874, 101.2753539, 0.59275, -1.730e-04, 23.7726800, -0.09243, -1.170e-03 - }; - static double[] LE2401 = new double[] { + }; + static readonly Double[] LE2401 = new Double[] { // 2401 6 26 2598183.958214, 11.0, 1059.4, 2.107, 1.139, 1, 5.31800, 0.99489, 0.27108, @@ -13176,7 +13174,7 @@ namespace CoordinateSharp.Eclipse 348.3334732, 0.58130, -3.870e-04, -5.5431301, 0.18349, 2.190e-04 }; - static double[] LE2501 = new double[] { + static readonly Double[] LE2501 = new Double[] { // 2501 3 6 2634595.801080, 7.0, 1461.0, 0.491, -0.599, 3, 17.93340, 0.90700, 0.24714, @@ -14541,70 +14539,49 @@ namespace CoordinateSharp.Eclipse 8.0835801, 0.28700, -3.300e-04 }; - public static double[] LunarDateData(DateTime d) - { - //Return combined 100 year arrays so in order to grab Last and Next exlipLE. - List data = new List() - { + public static Double[] LunarDateData(DateTime d) { + //Return combined 100 year arrays so in order to grab Last and Next exlipLE. + List data = new List() + { LE1601, LE1701,LE1801, LE1901, LE2001, LE2101,LE2201, LE2301, LE2401, LE2501 }; - double cent = Math.Floor(d.Year * .01) * 100; //Gets turn of century year. - int index = GetIndex(cent); //Gets index for calling data list. + Double cent = Math.Floor(d.Year * .01) * 100; //Gets turn of century year. + Int32 index = GetIndex(cent); //Gets index for calling data list. - if (index == -1) { return new double[] { }; } //RETURN EMPTY ARRAY IF OUTSIDE DB RANGE + if (index == -1) { return new Double[] { }; } //RETURN EMPTY ARRAY IF OUTSIDE DB RANGE - //Determine data to LEnd if year is near beginning or end of databaLE - int halfCent = d.Year - (int)cent; - if (index == 0 || index == data.Count - 1) - { - if (index == 0) - { - if (halfCent <= 50) { return data[0]; } - else { return data[0].Concat(data[1]).ToArray(); } - } - else - { - if (halfCent <= 50) { return data[index - 1].Concat(data[index]).ToArray(); } - else { return data[index]; } - } - } - else - { - if (halfCent <= 50) { return data[index - 1].Concat(data[index]).ToArray(); } - else { return data[index].Concat(data[index + 1]).ToArray(); } - } + //Determine data to LEnd if year is near beginning or end of databaLE + Int32 halfCent = d.Year - (Int32)cent; + return index == 0 || index == data.Count - 1 ? index == 0 ? halfCent <= 50 ? data[0] : data[0].Concat(data[1]).ToArray() : halfCent <= 50 ? data[index - 1].Concat(data[index]).ToArray() : data[index] : halfCent <= 50 ? data[index - 1].Concat(data[index]).ToArray() : data[index].Concat(data[index + 1]).ToArray(); - } - public static double[] LunarDateData_100Year(DateTime d) - { - //Return combined 100 year arrays - List data = new List() - { - LE1601, LE1701,LE1801, LE1901, LE2001, - LE2101,LE2201, LE2301, LE2401, LE2501 - }; - double cent = Math.Floor(d.Year * .01) * 100; //Gets turn of century year. - int index = GetIndex(cent); //Gets index for calling data list. - - if (index == -1) { return new double[] { }; } //RETURN EMPTY ARRAY IF OUTSIDE DB RANGE - //Return proper 100 year table. - return data[index]; - - } - private static int GetIndex(double cent) - { - int dex = 0; - int c = Convert.ToInt32(cent * .01); - //START CENTURY 16 - //END CENTRURY 26 - //AJDUST AS DATABALE GROWS - for (int i = 16; i < 26; i++) - { - if (i == c) { return dex; } - dex++; - } - return -1; - } } + public static Double[] LunarDateData_100Year(DateTime d) { + //Return combined 100 year arrays + List data = new List() + { + LE1601, LE1701,LE1801, LE1901, LE2001, + LE2101,LE2201, LE2301, LE2401, LE2501 + }; + Double cent = Math.Floor(d.Year * .01) * 100; //Gets turn of century year. + Int32 index = GetIndex(cent); //Gets index for calling data list. + + if (index == -1) { return new Double[] { }; } //RETURN EMPTY ARRAY IF OUTSIDE DB RANGE + //Return proper 100 year table. + return data[index]; + + } + private static Int32 GetIndex(Double cent) { + Int32 dex = 0; + Int32 c = Convert.ToInt32(cent * .01); + //START CENTURY 16 + //END CENTRURY 26 + //AJDUST AS DATABALE GROWS + for (Int32 i = 16; i < 26; i++) { + if (i == c) { return dex; } + dex++; + } + return -1; + } + } } diff --git a/CoordinateSharp/Eclipse/SolarData.cs b/CoordinateSharp/Eclipse/SolarData.cs index ac92053..d892729 100644 --- a/CoordinateSharp/Eclipse/SolarData.cs +++ b/CoordinateSharp/Eclipse/SolarData.cs @@ -2,13 +2,11 @@ using System.Collections.Generic; using System.Linq; -namespace CoordinateSharp.Eclipse -{ - internal class SolarData - { - //ECLIPSE DATA FROM 1701-2400 - static double[] SE1601 = new double[] - { +namespace CoordinateSharp.Eclipse { + internal class SolarData { + //ECLIPSE DATA FROM 1701-2400 + static readonly Double[] SE1601 = new Double[] + { // 1601 1 4 2305817.017109, 12.0, -4.0, 4.0, 117.4, 117.4, -0.2585420, 0.5087563, -7.100e-06, -6.160e-06, @@ -2241,9 +2239,9 @@ namespace CoordinateSharp.Eclipse 0.5346340, 0.0000437, -1.270e-05, -0.0114460, 0.0000434, -1.270e-05, 0.0046524, 0.0046293 - }; - static double[] SE1701 = new double[] - { + }; + static readonly Double[] SE1701 = new Double[] + { // 1701 2 7 2342375.461729, 23.0, -4.0, 4.0, 8.2, 8.2, -0.1836620, 0.4942406, -1.910e-05, -5.580e-06, @@ -4504,8 +4502,8 @@ namespace CoordinateSharp.Eclipse -0.0039760, -0.0000963, -1.230e-05, 0.0046973, 0.0046739 }; - static double[] SE1801 = new double[] - { + static readonly Double[] SE1801 = new Double[] + { // 1801 3 14 2378934.156652, 16.0, -4.0, 4.0, 13.0, 13.0, 0.8100150, 0.4830337, -6.270e-05, -6.820e-06, @@ -6684,8 +6682,8 @@ namespace CoordinateSharp.Eclipse 0.5676560, -0.0000826, -1.030e-05, 0.0214110, -0.0000822, -1.030e-05, 0.0047365, 0.0047129}; - static double[] SE1901 = new double[] - { + static readonly Double[] SE1901 = new Double[] + { // 1901 5 18 2415522.731807, 6.0, -4.0, 4.0, -0.9, -0.9, 0.3000790, 0.5746928, -4.400e-06, -9.600e-06, @@ -8738,9 +8736,9 @@ namespace CoordinateSharp.Eclipse 0.5730920, 0.0000514, -1.000e-05, 0.0268200, 0.0000511, -1.000e-05, 0.0047553, 0.0047316 - }; - static double[] SE2001 = new double[] - { + }; + static readonly Double[] SE2001 = new Double[] + { // 2001 6 21 2452082.003314, 12.0, -4.0, 4.0, 64.2, 64.2, 0.0103400, 0.5653861, 2.920e-05, -8.860e-06, @@ -10757,9 +10755,9 @@ namespace CoordinateSharp.Eclipse 0.5398730, -0.0001031, -1.210e-05, -0.0062330, -0.0001026, -1.200e-05, 0.0046346, 0.0046116 - }; - static double[] SE2101 = new double[] - { + }; + static readonly Double[] SE2101 = new Double[] + { // 2101 2 28 2488492.594741, 2.0, -4.0, 4.0, 205.2, 205.2, -0.5934920, 0.4746287, -1.290e-05, -6.230e-06, @@ -12875,9 +12873,9 @@ namespace CoordinateSharp.Eclipse 0.5652510, -0.0000740, -1.020e-05, 0.0190180, -0.0000736, -1.010e-05, 0.0046769, 0.0046536 - }; - static double[] SE2201 = new double[] - { + }; + static readonly Double[] SE2201 = new Double[] + { // 2201 4 4 2525051.763849, 6.0, -4.0, 4.0, 444.4, 444.4, -0.1405310, 0.5551418, 1.180e-05, -9.340e-06, @@ -15110,9 +15108,9 @@ namespace CoordinateSharp.Eclipse 0.5747280, 0.0000236, -9.900e-06, 0.0284480, 0.0000235, -9.800e-06, 0.0047462, 0.0047226 - }; - static double[] SE2301 = new double[] - { + }; + static readonly Double[] SE2301 = new Double[] + { // 2301 5 9 2561611.084014, 14.0, -4.0, 4.0, 719.9, 719.9, 0.2556460, 0.5255338, 4.360e-05, -8.080e-06, @@ -17345,8 +17343,8 @@ namespace CoordinateSharp.Eclipse 0.5389710, -0.0001064, -1.190e-05, -0.0071310, -0.0001059, -1.190e-05, 0.0046006, 0.0045776 - }; - static double[] SE2401 = new double[] { + }; + static readonly Double[] SE2401 = new Double[] { // 2401 1 14 2598021.427310, 22.0, -4.0, 4.0, 1057.8, 1057.8, -0.2870830, 0.5189539, -4.590e-05, -7.000e-06, @@ -19481,7 +19479,7 @@ namespace CoordinateSharp.Eclipse 0.0163870, -0.0000706, -1.000e-05, 0.0046201, 0.0045971 }; - static double[] SE2501 = new double[] { + static readonly Double[] SE2501 = new Double[] { // 2501 2 19 2634580.691215, 5.0, -4.0, 4.0, 1460.8, 1460.8, 0.1813720, 0.5619451, -3.330e-05, -9.490e-06, @@ -21508,71 +21506,50 @@ namespace CoordinateSharp.Eclipse 0.0248230, 0.0000365, -9.800e-06, 0.0046944, 0.0046710 }; - public static double[] SolarDateData(DateTime d) - { - //Return combined 100 year arrays so in order to grab Last and Next exlipse. - List data = new List() - { + public static Double[] SolarDateData(DateTime d) { + //Return combined 100 year arrays so in order to grab Last and Next exlipse. + List data = new List() + { SE1601, SE1701, SE1801, SE1901, SE2001, SE2101, SE2201, SE2301, SE2401, SE2501 }; - double cent = Math.Floor(d.Year * .01) * 100; //Gets turn of century year. - int index = GetIndex(cent); //Gets index for calling data list. + Double cent = Math.Floor(d.Year * .01) * 100; //Gets turn of century year. + Int32 index = GetIndex(cent); //Gets index for calling data list. - if (index == -1) { return new double[] { };} //RETURN EMPTY ARRAY IF OUTSIDE DB RANGE + if (index == -1) { return new Double[] { }; } //RETURN EMPTY ARRAY IF OUTSIDE DB RANGE - //Determine data to send if year is near beginning or end of database - int halfCent = d.Year - (int)cent; - - if (index == 0 || index == data.Count - 1) - { - if(index == 0) - { - if (halfCent <= 50) { return data[0]; } - else { return data[0].Concat(data[1]).ToArray(); } - } - else - { - if (halfCent <= 50) { return data[index - 1].Concat(data[index]).ToArray(); } - else { return data[index]; } - } - } - else - { - if (halfCent <= 50) { return data[index - 1].Concat(data[index]).ToArray(); } - else { return data[index].Concat(data[index+1]).ToArray(); } - } - - } - public static double[] SolarDateData_100Year(DateTime d) - { - //Return combined 100 year arrays - List data = new List() - { - SE1601, SE1701, SE1801, SE1901, SE2001, - SE2101, SE2201, SE2301, SE2401, SE2501 - }; - double cent = Math.Floor(d.Year * .01) * 100; //Gets turn of century year. - int index = GetIndex(cent); //Gets index for calling data list. + //Determine data to send if year is near beginning or end of database + Int32 halfCent = d.Year - (Int32)cent; - if (index == -1) { return new double[] { }; } //RETURN EMPTY ARRAY IF OUTSIDE DB RANGE - //Return proper 100 year table. - return data[index]; + return index == 0 || index == data.Count - 1 ? index == 0 ? halfCent <= 50 ? data[0] : data[0].Concat(data[1]).ToArray() : halfCent <= 50 ? data[index - 1].Concat(data[index]).ToArray() : data[index] : halfCent <= 50 ? data[index - 1].Concat(data[index]).ToArray() : data[index].Concat(data[index + 1]).ToArray(); - } - private static int GetIndex(double cent) - { - int dex = 0; - int c = Convert.ToInt32(cent * .01); - //START CENTURY 16 - //END CENTRURY 26 - //AJDUST AS DATABASE GROWS - for(int i = 16; i<26;i++) - { - if(i == c) { return dex; } - dex++; - } - return -1; - } } + public static Double[] SolarDateData_100Year(DateTime d) { + //Return combined 100 year arrays + List data = new List() + { + SE1601, SE1701, SE1801, SE1901, SE2001, + SE2101, SE2201, SE2301, SE2401, SE2501 + }; + Double cent = Math.Floor(d.Year * .01) * 100; //Gets turn of century year. + Int32 index = GetIndex(cent); //Gets index for calling data list. + + if (index == -1) { return new Double[] { }; } //RETURN EMPTY ARRAY IF OUTSIDE DB RANGE + //Return proper 100 year table. + return data[index]; + + } + private static Int32 GetIndex(Double cent) { + Int32 dex = 0; + Int32 c = Convert.ToInt32(cent * .01); + //START CENTURY 16 + //END CENTRURY 26 + //AJDUST AS DATABASE GROWS + for (Int32 i = 16; i < 26; i++) { + if (i == c) { return dex; } + dex++; + } + return -1; + } + } } diff --git a/CoordinateSharp/GeoFence.cs b/CoordinateSharp/GeoFence.cs index b576cd7..a238857 100644 --- a/CoordinateSharp/GeoFence.cs +++ b/CoordinateSharp/GeoFence.cs @@ -1,161 +1,138 @@ -using System.Collections.Generic; -using System.Linq; +using System; +using System.Collections.Generic; + +namespace CoordinateSharp { + /// + /// Geo Fence class. It helps to check if points/coordinates are inside a polygon, + /// Next to a polyline, and counting... + /// + public class GeoFence { + #region Fields + private readonly List _points = new List(); + #endregion -namespace CoordinateSharp -{ /// - /// Geo Fence class. It helps to check if points/coordinates are inside a polygon, - /// Next to a polyline, and counting... + /// Prepare GeoFence with a list of points /// - public class GeoFence - { - #region Fields - private List _points = new List(); - #endregion + /// List of points + public GeoFence(List points) => this._points = points; - /// - /// Prepare GeoFence with a list of points - /// - /// List of points - public GeoFence(List points) - { - _points = points; - } - - /// - /// Prepare Geofence with a list of coordinates - /// - /// List of coordinates - public GeoFence(List coordinates) - { - foreach (var c in coordinates) - { - _points.Add(new Point { Latitude = c.Latitude.ToDouble(), Longitude = c.Longitude.ToDouble() }); - } - } - - #region Utils - private Coordinate ClosestPointOnSegment(Point a, Point b, Coordinate p) - { - var d = new Point - { - Longitude = b.Longitude - a.Longitude, - Latitude = b.Latitude - a.Latitude, - }; - - double number = (p.Longitude.ToDouble() - a.Longitude) * d.Longitude + (p.Latitude.ToDouble() - a.Latitude) * d.Latitude; - - if (number <= 0.0) - return new Coordinate(a.Latitude, a.Longitude); - - double denom = d.Longitude * d.Longitude + d.Latitude * d.Latitude; - - if (number >= denom) - return new Coordinate(b.Latitude, b.Longitude); - - return new Coordinate(a.Latitude + (number / denom) * d.Latitude, a.Longitude + (number / denom) * d.Longitude); - } - #endregion - - /// - /// The function will return true if the point x,y is inside the polygon, or - /// false if it is not. If the point is exactly on the edge of the polygon, - /// then the function may return true or false. - /// - /// The point to test - /// bool - public bool IsPointInPolygon(Coordinate point) - { - if (point == null) - return false; - - double latitude = point.Latitude.ToDouble(); - double longitude = point.Longitude.ToDouble(); - int sides = _points.Count; - int j = sides - 1; - bool pointStatus = false; - for (int i = 0; i < sides; i++) - { - if (_points[i].Latitude < latitude && _points[j].Latitude >= latitude || _points[j].Latitude < latitude && _points[i].Latitude >= latitude) - { - if (_points[i].Longitude + (latitude - _points[i].Latitude) / (_points[j].Latitude - _points[i].Latitude) * (_points[j].Longitude - _points[i].Longitude) < longitude) - { - pointStatus = !pointStatus; - } - } - j = i; - } - return pointStatus; - } - - /// - /// The function will return true if the point x,y is next the given range of - /// the polyline, or false if it is not. - /// - /// The point to test - /// The range in meters - /// bool - public bool IsPointInRangeOfLine(Coordinate point, double range) - { - if (point == null) - return false; - - for (int i = 0; i < _points.Count - 1; i++) - { - Coordinate c = ClosestPointOnSegment(_points[i], _points[i + 1], point); - if (c.Get_Distance_From_Coordinate(point).Meters <= range) - return true; - } - - return false; - } - - /// - /// The function will return true if the point x,y is next the given range of - /// the polyline, or false if it is not. - /// - /// The point to test - /// The range is a distance object - /// bool - public bool IsPointInRangeOfLine(Coordinate point, Distance range) - { - if (point == null || range == null) - return false; - - return IsPointInRangeOfLine(point, range.Meters); - } - - /// - /// This class is a help class to simplify GeoFence calculus - /// - public class Point - { - - /// - /// Initialize empty point - /// - public Point() - { - - } - /// - /// Initialize point with defined Latitude and Longitude - /// - /// Latitude (signed) - /// Longitude (signed) - public Point(double lat, double lng) - { - Latitude = lat; - Longitude = lng; - } - /// - /// The longitude in degrees - /// - public double Longitude; - /// - /// The latitude in degrees - /// - public double Latitude; - } + /// + /// Prepare Geofence with a list of coordinates + /// + /// List of coordinates + public GeoFence(List coordinates) { + foreach (Coordinate c in coordinates) { + this._points.Add(new Point { Latitude = c.Latitude.ToDouble(), Longitude = c.Longitude.ToDouble() }); + } } + + #region Utils + private Coordinate ClosestPointOnSegment(Point a, Point b, Coordinate p) { + Point d = new Point { + Longitude = b.Longitude - a.Longitude, + Latitude = b.Latitude - a.Latitude, + }; + + Double number = (p.Longitude.ToDouble() - a.Longitude) * d.Longitude + (p.Latitude.ToDouble() - a.Latitude) * d.Latitude; + + if (number <= 0.0) { + return new Coordinate(a.Latitude, a.Longitude); + } + + Double denom = d.Longitude * d.Longitude + d.Latitude * d.Latitude; + + return number >= denom ? new Coordinate(b.Latitude, b.Longitude) : new Coordinate(a.Latitude + number / denom * d.Latitude, a.Longitude + number / denom * d.Longitude); + } + #endregion + + /// + /// The function will return true if the point x,y is inside the polygon, or + /// false if it is not. If the point is exactly on the edge of the polygon, + /// then the function may return true or false. + /// + /// The point to test + /// bool + public Boolean IsPointInPolygon(Coordinate point) { + if (point == null) { + return false; + } + + Double latitude = point.Latitude.ToDouble(); + Double longitude = point.Longitude.ToDouble(); + Int32 sides = this._points.Count; + Int32 j = sides - 1; + Boolean pointStatus = false; + for (Int32 i = 0; i < sides; i++) { + if (this._points[i].Latitude < latitude && this._points[j].Latitude >= latitude || this._points[j].Latitude < latitude && this._points[i].Latitude >= latitude) { + if (this._points[i].Longitude + (latitude - this._points[i].Latitude) / (this._points[j].Latitude - this._points[i].Latitude) * (this._points[j].Longitude - this._points[i].Longitude) < longitude) { + pointStatus = !pointStatus; + } + } + j = i; + } + return pointStatus; + } + + /// + /// The function will return true if the point x,y is next the given range of + /// the polyline, or false if it is not. + /// + /// The point to test + /// The range in meters + /// bool + public Boolean IsPointInRangeOfLine(Coordinate point, Double range) { + if (point == null) { + return false; + } + + for (Int32 i = 0; i < this._points.Count - 1; i++) { + Coordinate c = this.ClosestPointOnSegment(this._points[i], this._points[i + 1], point); + if (c.Get_Distance_From_Coordinate(point).Meters <= range) { + return true; + } + } + + return false; + } + + /// + /// The function will return true if the point x,y is next the given range of + /// the polyline, or false if it is not. + /// + /// The point to test + /// The range is a distance object + /// bool + public Boolean IsPointInRangeOfLine(Coordinate point, Distance range) => point == null || range == null ? false : this.IsPointInRangeOfLine(point, range.Meters); + + /// + /// This class is a help class to simplify GeoFence calculus + /// + public class Point { + + /// + /// Initialize empty point + /// + public Point() { + + } + /// + /// Initialize point with defined Latitude and Longitude + /// + /// Latitude (signed) + /// Longitude (signed) + public Point(Double lat, Double lng) { + this.Latitude = lat; + this.Longitude = lng; + } + /// + /// The longitude in degrees + /// + public Double Longitude; + /// + /// The latitude in degrees + /// + public Double Latitude; + } + } } \ No newline at end of file diff --git a/CoordinateSharp/Properties/AssemblyInfo.cs b/CoordinateSharp/Properties/AssemblyInfo.cs index 1c1f393..fae1d09 100644 --- a/CoordinateSharp/Properties/AssemblyInfo.cs +++ b/CoordinateSharp/Properties/AssemblyInfo.cs @@ -1,4 +1,5 @@ -using System.Reflection; +#if !NETCOREAPP +using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -34,3 +35,4 @@ using System.Runtime.InteropServices; // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.1.5.2")] [assembly: AssemblyFileVersion("1.1.5.2")] +#endif \ No newline at end of file diff --git a/CoordinateSharp_Core.sln b/CoordinateSharp_Core.sln new file mode 100644 index 0000000..96bf25b --- /dev/null +++ b/CoordinateSharp_Core.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29519.87 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CoordinateSharp", "CoordinateSharp\CoordinateSharp_Core.csproj", "{127E1C0F-06DC-492C-B124-19A89D44B47C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {127E1C0F-06DC-492C-B124-19A89D44B47C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {127E1C0F-06DC-492C-B124-19A89D44B47C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {127E1C0F-06DC-492C-B124-19A89D44B47C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {127E1C0F-06DC-492C-B124-19A89D44B47C}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {667C979D-42C3-4EE8-9B2B-D85EFC09940A} + EndGlobalSection +EndGlobal