Init CordinateSharp as library
This commit is contained in:
commit
570ec853de
31
CoordinateSharp.sln
Normal file
31
CoordinateSharp.sln
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio 15
|
||||||
|
VisualStudioVersion = 15.0.27703.2026
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CoordinateSharp", "CoordinateSharp\CoordinateSharp.csproj", "{82B32704-3306-49FD-A7FA-DB48B67B7B64}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CoordinateSharp_TestProj", "CoordinateSharp_TestProj\CoordinateSharp_TestProj.csproj", "{B4E46A02-7C45-4A13-8D62-F99FCA853BDC}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{82B32704-3306-49FD-A7FA-DB48B67B7B64}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{82B32704-3306-49FD-A7FA-DB48B67B7B64}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{82B32704-3306-49FD-A7FA-DB48B67B7B64}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{82B32704-3306-49FD-A7FA-DB48B67B7B64}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{B4E46A02-7C45-4A13-8D62-F99FCA853BDC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{B4E46A02-7C45-4A13-8D62-F99FCA853BDC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{B4E46A02-7C45-4A13-8D62-F99FCA853BDC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{B4E46A02-7C45-4A13-8D62-F99FCA853BDC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {580EC371-2895-4067-9F0F-9E3E9ABED545}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
1317
CoordinateSharp/Celestial.Assistant.cs
Normal file
1317
CoordinateSharp/Celestial.Assistant.cs
Normal file
File diff suppressed because it is too large
Load Diff
412
CoordinateSharp/Celestial.LunarEclipseCalc.cs
Normal file
412
CoordinateSharp/Celestial.LunarEclipseCalc.cs
Normal file
@ -0,0 +1,412 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
//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.
|
||||||
|
|
||||||
|
//CURRENT RANGE 1601-2600.
|
||||||
|
internal class LunarEclipseCalc
|
||||||
|
{
|
||||||
|
public static List<List<string>> CalculateLunarEclipse(DateTime d, double latRad, double longRad)
|
||||||
|
{
|
||||||
|
return Calculate(d, latRad, longRad);
|
||||||
|
}
|
||||||
|
public static List<LunarEclipseDetails> CalculateLunarEclipse(DateTime d, double latRad, double longRad, double[] events)
|
||||||
|
{
|
||||||
|
List<List<string>> evs = Calculate(d, latRad, longRad, events);
|
||||||
|
List<LunarEclipseDetails> deetsList = new List<LunarEclipseDetails>();
|
||||||
|
foreach (List<string> ls in evs)
|
||||||
|
{
|
||||||
|
LunarEclipseDetails deets = new LunarEclipseDetails(ls);
|
||||||
|
deetsList.Add(deets);
|
||||||
|
}
|
||||||
|
return deetsList;
|
||||||
|
}
|
||||||
|
public static List<List<string>> CalculateLunarEclipse(DateTime d, Coordinate coord)
|
||||||
|
{
|
||||||
|
return Calculate(d, coord.Latitude.ToRadians(), coord.Longitude.ToRadians());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// CALCULATE!
|
||||||
|
private static List<List<string>> 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<List<string>> events = new List<List<string>>();
|
||||||
|
|
||||||
|
double[] el;
|
||||||
|
if (ev == null)
|
||||||
|
{
|
||||||
|
el = Eclipse.LunarData.LunarDateData(d);//Get 100 year solar data;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
el = ev;
|
||||||
|
}
|
||||||
|
|
||||||
|
events = new List<List<string>>();
|
||||||
|
ReadData(latRad, longRad, obsvconst);
|
||||||
|
|
||||||
|
for (int i = 0; i < el.Length; i += 22)
|
||||||
|
{
|
||||||
|
if (el[5 + i] <= obsvconst[5])
|
||||||
|
{
|
||||||
|
List<string> values = new List<string>();
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
221
CoordinateSharp/Celestial.MeeusTables.cs
Normal file
221
CoordinateSharp/Celestial.MeeusTables.cs
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace CoordinateSharp
|
||||||
|
{
|
||||||
|
internal partial class MeeusTables
|
||||||
|
{
|
||||||
|
//Ch 47
|
||||||
|
private static double[] Table47A_Arguments = new double[]
|
||||||
|
{
|
||||||
|
0,0,1,0,
|
||||||
|
2,0,-1,0,
|
||||||
|
2,0,0,0,
|
||||||
|
0,0,2,0,
|
||||||
|
0,1,0,0,
|
||||||
|
0,0,0,2,
|
||||||
|
2,0,-2,0,
|
||||||
|
2,-1,-1,0,
|
||||||
|
2,0,1,0,
|
||||||
|
2,-1,0,0,
|
||||||
|
0,1,-1,0,
|
||||||
|
1,0,0,0,
|
||||||
|
0,1,1,0,
|
||||||
|
2,0,0,-2,
|
||||||
|
0,0,1,2,
|
||||||
|
0,0,1,-2,
|
||||||
|
4,0,-1,0,
|
||||||
|
0,0,3,0,
|
||||||
|
4,0,-2,0,
|
||||||
|
2,1,-1,0,
|
||||||
|
2,1,0,0,
|
||||||
|
1,0,-1,0,
|
||||||
|
1,1,0,0,
|
||||||
|
2,-1,1,0,
|
||||||
|
2,0,2,0,
|
||||||
|
4,0,0,0,
|
||||||
|
2,0,-3,0,
|
||||||
|
0,1,-2,0,
|
||||||
|
2,0,-1,2,
|
||||||
|
2,-1,-2,0,
|
||||||
|
1,0,1,0,
|
||||||
|
2,-2,0,0,
|
||||||
|
|
||||||
|
0,1,2,0,
|
||||||
|
0,2,0,0,
|
||||||
|
2,-2,-1,0,
|
||||||
|
2,0,1,-2,
|
||||||
|
2,0,0,2,
|
||||||
|
4,-1,-1,0,
|
||||||
|
0,0,2,2,
|
||||||
|
3,0,-1,0,
|
||||||
|
2,1,1,0,
|
||||||
|
4,-1,-2,0,
|
||||||
|
0,2,-1,0,
|
||||||
|
2,2,-1,0,
|
||||||
|
2,1,-2,0,
|
||||||
|
2,-1,0,-2,
|
||||||
|
4,0,1,0,
|
||||||
|
0,0,4,0,
|
||||||
|
4,-1,0,0,
|
||||||
|
1,0,-2,0,
|
||||||
|
2,1,0,-2,
|
||||||
|
0,0,2,-2,
|
||||||
|
1,1,1,0,
|
||||||
|
3,0,-2,0,
|
||||||
|
4,0,-3,0,
|
||||||
|
2,-1,2,0,
|
||||||
|
0,2,1,0,
|
||||||
|
1,1,-1,0,
|
||||||
|
2,0,3,0,
|
||||||
|
2,0,-1,-2
|
||||||
|
};
|
||||||
|
private static double[] Table47B_Arguments = new double[]
|
||||||
|
{
|
||||||
|
0,0,0,1,
|
||||||
|
0,0,1,1,
|
||||||
|
0,0,1,-1,
|
||||||
|
2,0,0,-1,
|
||||||
|
2,0,-1,1,
|
||||||
|
2,0,-1,-1,
|
||||||
|
2,0,0,1,
|
||||||
|
0,0,2,1,
|
||||||
|
2,0,1,-1,
|
||||||
|
0,0,2,-1,
|
||||||
|
2,-1,0,-1,
|
||||||
|
2,0,-2,-1,
|
||||||
|
2,0,1,1,
|
||||||
|
2,1,0,-1,
|
||||||
|
2,-1,-1,1,
|
||||||
|
2,-1,0,1,
|
||||||
|
2,-1,-1,-1,
|
||||||
|
0,1,-1,-1,
|
||||||
|
4,0,-1,-1,
|
||||||
|
0,1,0,1,
|
||||||
|
0,0,0,3,
|
||||||
|
0,1,-1,1,
|
||||||
|
1,0,0,1,
|
||||||
|
0,1,1,1,
|
||||||
|
0,1,1,-1,
|
||||||
|
0,1,0,-1,
|
||||||
|
1,0,0,-1,
|
||||||
|
0,0,3,1,
|
||||||
|
4,0,0,-1,
|
||||||
|
4,0,-1,1,
|
||||||
|
|
||||||
|
0,0,1,-3,
|
||||||
|
4,0,-2,1,
|
||||||
|
2,0,0,-3,
|
||||||
|
2,0,2,-1,
|
||||||
|
2,-1,1,-1,
|
||||||
|
2,0,-2,1,
|
||||||
|
0,0,3,-1,
|
||||||
|
2,0,2,1,
|
||||||
|
2,0,-3,-1,
|
||||||
|
2,1,-1,1,
|
||||||
|
2,1,0,1,
|
||||||
|
4,0,0,1,
|
||||||
|
2,-1,1,1,
|
||||||
|
2,-2,0,-1,
|
||||||
|
0,0,1,3,
|
||||||
|
2,1,1,-1,
|
||||||
|
1,1,0,-1,
|
||||||
|
1,1,0,1,
|
||||||
|
0,1,-2,-1,
|
||||||
|
2,1,-1,-1,
|
||||||
|
1,0,1,1,
|
||||||
|
2,-1,-2,-1,
|
||||||
|
0,1,2,1,
|
||||||
|
4,0,-2,-1,
|
||||||
|
4,-1,-1,-1,
|
||||||
|
1,0,1,-1,
|
||||||
|
4,0,1,-1,
|
||||||
|
1,0,-1,-1,
|
||||||
|
4,-1,0,-1,
|
||||||
|
2,-2,0,1,
|
||||||
|
};
|
||||||
|
private static 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,
|
||||||
|
4987,4036,3994,3861,3665,-2689,-2602,2390,-2348,2236,-2120,-2069,2048,-1773,
|
||||||
|
-1595,1215,-1110,-892,-810,759,-713,-700,691,596,549,537,520,-487,-399,-381,
|
||||||
|
351,-340,330,327,-323,299,294,0,
|
||||||
|
//Er
|
||||||
|
-20905355,-3699111,-2955968,-569925,48888,-3149,246158,-152138,-170733,-204586,
|
||||||
|
-129620,108743,104755,10321,0,79661,-34782,-23210,-21636,24208,30824,-8379,-16675,
|
||||||
|
-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[]
|
||||||
|
{
|
||||||
|
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.
|
||||||
|
|
||||||
|
int nl = l * 4;
|
||||||
|
|
||||||
|
if (sine)
|
||||||
|
{
|
||||||
|
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] * 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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private static double Get_Table47B_Values(double[] values, int l, double t)
|
||||||
|
{
|
||||||
|
int 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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
872
CoordinateSharp/Celestial.MoonCalculations.cs
Normal file
872
CoordinateSharp/Celestial.MoonCalculations.cs
Normal file
@ -0,0 +1,872 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace CoordinateSharp
|
||||||
|
{
|
||||||
|
internal class MoonCalc
|
||||||
|
{
|
||||||
|
static double rad = Math.PI / 180; //For converting radians
|
||||||
|
|
||||||
|
//obliquity of the ecliptic in radians based on standard equinox 2000.
|
||||||
|
static double e = rad * 23.4392911;
|
||||||
|
/// <summary>
|
||||||
|
/// Gets Moon Times, Altitude and Azimuth
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="date">Date</param>
|
||||||
|
/// <param name="lat">Latitude</param>
|
||||||
|
/// <param name="lng">Longitude</param>
|
||||||
|
/// <param name="c">Celestial</param>
|
||||||
|
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 && 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 (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 && 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 (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 && 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<List<string>> 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<string> 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
|
||||||
|
P = rad * 102.9372; // perihelion of the Earth
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Grabs Perigee or Apogee of Moon based on specified time.
|
||||||
|
/// Results will return event just before, or just after specified DateTime
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="d">DateTime</param>
|
||||||
|
/// <param name="md">Event Type</param>
|
||||||
|
/// <returns>PerigeeApogee</returns>
|
||||||
|
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.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
|
||||||
|
|
||||||
|
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
|
||||||
|
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 -
|
||||||
|
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 -
|
||||||
|
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 -
|
||||||
|
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 -
|
||||||
|
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; }
|
||||||
|
|
||||||
|
//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);
|
||||||
|
|
||||||
|
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 (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);
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets moon distance (Ch 47).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="d">DateTime</param>
|
||||||
|
/// <returns>Distance</returns>
|
||||||
|
public static Distance GetMoonDistance(DateTime d)
|
||||||
|
{
|
||||||
|
//Ch 47
|
||||||
|
double JDE = JulianConversions.GetJulian(d);//Get julian
|
||||||
|
double T = (JDE - 2451545) / 36525; //Get dynamic time.
|
||||||
|
|
||||||
|
double[] values = Get_Moon_LDMNF(T);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 dist = 385000.56 + (MeeusTables.Moon_Periodic_Er(D, M, N, F, T) / 1000);
|
||||||
|
return new Distance(dist);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets Moon L, D, M, N, F values
|
||||||
|
/// Ch. 47
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="T">Dynamic Time</param>
|
||||||
|
/// <returns>double[] containing L,D,M,N,F</returns>
|
||||||
|
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 -
|
||||||
|
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 -
|
||||||
|
.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) +
|
||||||
|
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) /
|
||||||
|
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;
|
||||||
|
|
||||||
|
return new double[] { L, D, M, N, F };
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Get moons lat/long in radians (Ch 47).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="LDMNF">L,D,M,N,F</param>
|
||||||
|
/// <param name="T">Dynamic Time</param>
|
||||||
|
/// <returns>Lat[0], Long[1]</returns>
|
||||||
|
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 };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets right Ascension of celestial object (Ch 13 Fig 13.3)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="l">latitude in radians</param>
|
||||||
|
/// <param name="b">longitude in radian</param>
|
||||||
|
/// <returns>Right Ascension</returns>
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Gets declination of celestial object (Ch 13 Fig 13.4)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="l">latitude in radians</param>
|
||||||
|
/// <param name="b">longitude in radian</param>
|
||||||
|
/// <returns>Declination</returns>
|
||||||
|
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;
|
||||||
|
|
||||||
|
}
|
||||||
|
static double Parallax_RA(double distance, double H, double pCosE, double dec, double ra)
|
||||||
|
{
|
||||||
|
//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) { 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
971
CoordinateSharp/Celestial.SolarEclipseCalc.cs
Normal file
971
CoordinateSharp/Celestial.SolarEclipseCalc.cs
Normal file
@ -0,0 +1,971 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
//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.
|
||||||
|
|
||||||
|
//CURRENT RANGE 1601-2600.
|
||||||
|
internal class SolarEclipseCalc
|
||||||
|
{
|
||||||
|
public static List<List<string>> CalculateSolarEclipse(DateTime d, double latRad, double longRad)
|
||||||
|
{
|
||||||
|
return Calculate(d, latRad, longRad, null);
|
||||||
|
}
|
||||||
|
public static List<SolarEclipseDetails> CalculateSolarEclipse(DateTime d, double latRad, double longRad, double[] events)
|
||||||
|
{
|
||||||
|
List<List<string>> evs = Calculate(d, latRad, longRad, events);
|
||||||
|
List<SolarEclipseDetails> deetsList = new List<SolarEclipseDetails>();
|
||||||
|
foreach(List<string> ls in evs)
|
||||||
|
{
|
||||||
|
SolarEclipseDetails deets = new SolarEclipseDetails(ls);
|
||||||
|
deetsList.Add(deets);
|
||||||
|
}
|
||||||
|
return deetsList;
|
||||||
|
}
|
||||||
|
public static List<List<string>> CalculateSolarEclipse(DateTime d, Coordinate coord)
|
||||||
|
{
|
||||||
|
return Calculate(d, coord.Latitude.ToRadians(), coord.Longitude.ToRadians(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<List<string>> 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<List<string>> events = new List<List<string>>();
|
||||||
|
double[] el;
|
||||||
|
if (ev == null)
|
||||||
|
{
|
||||||
|
el = Eclipse.SolarData.SolarDateData(d);//Get 100 year solar data;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
el = ev;
|
||||||
|
}
|
||||||
|
|
||||||
|
events = new List<List<string>>();
|
||||||
|
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<string> values = new List<string>();
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
374
CoordinateSharp/Celestial.SunCalculations.cs
Normal file
374
CoordinateSharp/Celestial.SunCalculations.cs
Normal file
@ -0,0 +1,374 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
//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;
|
||||||
|
|
||||||
|
}
|
||||||
|
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];
|
||||||
|
|
||||||
|
|
||||||
|
//Nautical
|
||||||
|
evDate = Get_Event_Time(lw, phi, -12, actualDate);
|
||||||
|
c.AdditionalSolarTimes.NauticalDawn = evDate[0];
|
||||||
|
c.AdditionalSolarTimes.NauticalDusk = evDate[1];
|
||||||
|
|
||||||
|
//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);
|
||||||
|
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Gets time of event based on specified degree below horizon
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="lw">Observer Longitude in radians</param>
|
||||||
|
/// <param name="phi">Observer Latitude in radians</param>
|
||||||
|
/// <param name="h">Angle in Degrees</param>
|
||||||
|
/// <param name="date">Date of Event</param>
|
||||||
|
/// <returns>DateTime?[]{rise, set}</returns>
|
||||||
|
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 };
|
||||||
|
}
|
||||||
|
|
||||||
|
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<List<string>> 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<string> 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)
|
||||||
|
{
|
||||||
|
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)); }
|
||||||
|
|
||||||
|
//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)
|
||||||
|
{
|
||||||
|
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
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
620
CoordinateSharp/Celestial.cs
Normal file
620
CoordinateSharp/Celestial.cs
Normal file
@ -0,0 +1,620 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
namespace CoordinateSharp
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The main class for handling location based celestial information.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This class can calculate various pieces of solar and lunar data, based on location and date
|
||||||
|
/// </remarks>
|
||||||
|
[Serializable]
|
||||||
|
public class Celestial
|
||||||
|
{
|
||||||
|
|
||||||
|
//When a rise or a set does not occur, the DateTime will return null
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates an empty Celestial.
|
||||||
|
/// </summary>
|
||||||
|
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)); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a Celestial based on a location and specified date
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="lat">latitude</param>
|
||||||
|
/// <param name="longi">longitude</param>
|
||||||
|
/// <param name="geoDate">DateTime (UTC)</param>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a Celestial based on a location and date in the provided Coordinate.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="c">Coordinate</param>
|
||||||
|
/// <returns>Celestial</returns>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts Celestial values to local times.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="c">Coordinate</param>
|
||||||
|
/// <param name="offset">UTC offset</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// In place time slip
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="c">Coordinate</param>
|
||||||
|
/// <param name="offset">hour offset</param>
|
||||||
|
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;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sunset time.
|
||||||
|
/// </summary>
|
||||||
|
public DateTime? SunSet { get { return sunSet; } }
|
||||||
|
/// <summary>
|
||||||
|
/// Sunrise time.
|
||||||
|
/// </summary>
|
||||||
|
public DateTime? SunRise { get { return sunRise; } }
|
||||||
|
/// <summary>
|
||||||
|
/// Moonset time.
|
||||||
|
/// </summary>
|
||||||
|
public DateTime? MoonSet { get { return moonSet; } }
|
||||||
|
/// <summary>
|
||||||
|
/// Moonrise time.
|
||||||
|
/// </summary>
|
||||||
|
public DateTime? MoonRise { get { return moonRise; } }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sun altitude in degrees (E of N).
|
||||||
|
/// </summary>
|
||||||
|
public double SunAltitude { get { return sunAltitude; } }
|
||||||
|
/// <summary>
|
||||||
|
/// Sun azimuth in degrees (E of N).
|
||||||
|
/// </summary>
|
||||||
|
public double SunAzimuth { get { return sunAzimuth; } }
|
||||||
|
/// <summary>
|
||||||
|
/// Moon altitude in degrees (corrected for parallax and refraction).
|
||||||
|
/// </summary>
|
||||||
|
public double MoonAltitude { get { return moonAltitude; } }
|
||||||
|
/// <summary>
|
||||||
|
/// Moon azimuth in degrees (E of N).
|
||||||
|
/// </summary>
|
||||||
|
public double MoonAzimuth { get { return moonAzimuth; } }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Estimated moon distance from the earth.
|
||||||
|
/// </summary>
|
||||||
|
public Distance MoonDistance { get { return moonDistance; } }
|
||||||
|
/// <summary>
|
||||||
|
/// Sun's Condition based on the provided date.
|
||||||
|
/// </summary>
|
||||||
|
public CelestialStatus SunCondition { get { return sunCondition; } }
|
||||||
|
/// <summary>
|
||||||
|
/// Moon's condition based on the provided date.
|
||||||
|
/// </summary>
|
||||||
|
public CelestialStatus MoonCondition { get { return moonCondition; } }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determine if the sun is currently up, based on sunset and sunrise time at the provided location and date.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsSunUp{ get { return isSunUp; } }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determine if the moon is currently up, based on moonset and moonrise time at the provided location and date.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsMoonUp { get { return isMoonUp; } }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Moon ilumination details based on the provided date.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Contains phase, phase name, fraction and angle
|
||||||
|
/// </remarks>
|
||||||
|
public MoonIllum MoonIllum { get { return moonIllum; } }
|
||||||
|
/// <summary>
|
||||||
|
/// Moons perigee details based on the provided date.
|
||||||
|
/// </summary>
|
||||||
|
public Perigee Perigee { get { return perigee; } }
|
||||||
|
/// <summary>
|
||||||
|
/// Moons apogee details based on the provided date.
|
||||||
|
/// </summary>
|
||||||
|
public Apogee Apogee { get { return apogee; } }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Additional solar event times based on the provided date and location.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Contains civil and nautical dawn and dusk times.</remarks>
|
||||||
|
public AdditionalSolarTimes AdditionalSolarTimes { get { return additionalSolarTimes; } }
|
||||||
|
/// <summary>
|
||||||
|
/// Astrological signs based on the provided date.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Contains zodiac, moon sign and moon name during full moon events
|
||||||
|
/// </remarks>
|
||||||
|
public AstrologicalSigns AstrologicalSigns { get { return astrologicalSigns; } }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a SolarEclipse.
|
||||||
|
/// </summary>
|
||||||
|
public SolarEclipse SolarEclipse { get { return solarEclipse; } }
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a LunarEclipse.
|
||||||
|
/// </summary>
|
||||||
|
public LunarEclipse LunarEclipse { get { return lunarEclipse; } }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculates all celestial data. Coordinates will notify as changes occur
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="lat">Decimal format latitude</param>
|
||||||
|
/// <param name="longi">Decimal format longitude</param>
|
||||||
|
/// <param name="date">Geographic DateTime</param>
|
||||||
|
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);
|
||||||
|
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Calculate celestial data based on lat/long and date.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="lat">Decimal format latitude</param>
|
||||||
|
/// <param name="longi">Decimal format longitude</param>
|
||||||
|
/// <param name="date">Geographic DateTime</param>
|
||||||
|
/// <returns>Fully populated Celestial object</returns>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculate sun data based on lat/long and date.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="lat">latitude</param>
|
||||||
|
/// <param name="longi">longitude</param>
|
||||||
|
/// <param name="date">DateTime</param>
|
||||||
|
/// <returns>Celestial (Partially Populated)</returns>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Calculate moon data based on lat/long and date.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="lat">latitude</param>
|
||||||
|
/// <param name="longi">longitude</param>
|
||||||
|
/// <param name="date">DateTime</param>
|
||||||
|
/// <returns>Celestial (Partially Populated)</returns>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a List containing solar eclipse data for the century.
|
||||||
|
/// Century return is based on the date passed.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="lat">latitude</param>
|
||||||
|
/// <param name="longi">longitude</param>
|
||||||
|
/// <param name="date">DateTime</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static List<SolarEclipseDetails> 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);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a List containing solar eclipse data for the century.
|
||||||
|
/// Century return is based on the date passed.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="lat">latitude</param>
|
||||||
|
/// <param name="longi">longitude</param>
|
||||||
|
/// <param name="date">DateTime</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static List<LunarEclipseDetails> 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set bool SunIsUp and MoonIsUp values
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="date">Coordinate GeoDate</param>
|
||||||
|
/// <param name="cel">Celestial Object</param>
|
||||||
|
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.SunSet)
|
||||||
|
{
|
||||||
|
cel.isSunUp = true;
|
||||||
|
}
|
||||||
|
else { cel.isSunUp = false; }
|
||||||
|
break;
|
||||||
|
case CelestialStatus.NoSet:
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns Apogee object containing last and next apogee based on the specified date.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="d">DateTime</param>
|
||||||
|
/// <returns>Apogee</returns>
|
||||||
|
public static Apogee GetApogees(DateTime d)
|
||||||
|
{
|
||||||
|
return MoonCalc.GetApogeeEvents(d);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Returns Perigee object containing last and next perigee based on the specified date.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="d">DateTime</param>
|
||||||
|
/// <returns>Perigee</returns>
|
||||||
|
public static Perigee GetPerigees(DateTime d)
|
||||||
|
{
|
||||||
|
return MoonCalc.GetPerigeeEvents(d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
269
CoordinateSharp/Coordinate.Assistant.cs
Normal file
269
CoordinateSharp/Coordinate.Assistant.cs
Normal file
@ -0,0 +1,269 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace CoordinateSharp
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used for UTM/MGRS Conversions
|
||||||
|
/// </summary>
|
||||||
|
[Serializable]
|
||||||
|
internal class LatZones
|
||||||
|
{
|
||||||
|
public static List<string> longZongLetters = new List<string>(new string[]{"C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T",
|
||||||
|
"U", "V", "W", "X"});
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Used for handling diagraph determination
|
||||||
|
/// </summary>
|
||||||
|
[Serializable]
|
||||||
|
internal class Digraphs
|
||||||
|
{
|
||||||
|
private List<Digraph> digraph1;
|
||||||
|
private List<Digraph> 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<Digraph>();
|
||||||
|
digraph2 = new List<Digraph>();
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Diagraph model
|
||||||
|
/// </summary>
|
||||||
|
[Serializable]
|
||||||
|
internal class Digraph
|
||||||
|
{
|
||||||
|
public int Zone { get; set; }
|
||||||
|
public string Letter { get; set; }
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Used for setting whether a coordinate part is latitudinal or longitudinal.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable]
|
||||||
|
public enum CoordinateType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Latitude
|
||||||
|
/// </summary>
|
||||||
|
Lat,
|
||||||
|
/// <summary>
|
||||||
|
/// Longitude
|
||||||
|
/// </summary>
|
||||||
|
Long
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Used to set a coordinate part position.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable]
|
||||||
|
public enum CoordinatesPosition :int
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// North
|
||||||
|
/// </summary>
|
||||||
|
N,
|
||||||
|
/// <summary>
|
||||||
|
/// East
|
||||||
|
/// </summary>
|
||||||
|
E,
|
||||||
|
/// <summary>
|
||||||
|
/// South
|
||||||
|
/// </summary>
|
||||||
|
S,
|
||||||
|
/// <summary>
|
||||||
|
/// West
|
||||||
|
/// </summary>
|
||||||
|
W
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Coordinate type datum specification
|
||||||
|
/// </summary>
|
||||||
|
[Serializable]
|
||||||
|
[Flags]
|
||||||
|
public enum Coordinate_Datum
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Lat Long GeoDetic
|
||||||
|
/// </summary>
|
||||||
|
LAT_LONG = 1,
|
||||||
|
/// <summary>
|
||||||
|
/// UTM and MGRS
|
||||||
|
/// </summary>
|
||||||
|
UTM_MGRS = 2,
|
||||||
|
/// <summary>
|
||||||
|
/// ECEF
|
||||||
|
/// </summary>
|
||||||
|
ECEF = 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cartesian Coordinate Type
|
||||||
|
/// </summary>
|
||||||
|
public enum CartesianType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Spherical Cartesian
|
||||||
|
/// </summary>
|
||||||
|
Cartesian,
|
||||||
|
/// <summary>
|
||||||
|
/// Earth Centered Earth Fixed
|
||||||
|
/// </summary>
|
||||||
|
ECEF,
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Used for easy read math functions
|
||||||
|
/// </summary>
|
||||||
|
[Serializable]
|
||||||
|
internal static class ModM
|
||||||
|
{
|
||||||
|
public static double Mod(double x, double y)
|
||||||
|
{
|
||||||
|
return x - y * Math.Floor(x / y);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Earth Shape for Calculations.
|
||||||
|
/// </summary>
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public enum Shape
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Calculate as sphere (less accurate, more efficient).
|
||||||
|
/// </summary>
|
||||||
|
Sphere,
|
||||||
|
/// <summary>
|
||||||
|
/// Calculate as ellipsoid (more accurate, less efficient).
|
||||||
|
/// </summary>
|
||||||
|
Ellipsoid
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
155
CoordinateSharp/Coordinate.Cartesian.cs
Normal file
155
CoordinateSharp/Coordinate.Cartesian.cs
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel;
|
||||||
|
namespace CoordinateSharp
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Cartesian (X, Y, Z) Coordinate
|
||||||
|
/// </summary>
|
||||||
|
[Serializable]
|
||||||
|
public class Cartesian : INotifyPropertyChanged
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Create a Cartesian Object
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="c"></param>
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Create a Cartesian Object
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="xc">X</param>
|
||||||
|
/// <param name="yc">Y</param>
|
||||||
|
/// <param name="zc">Z</param>
|
||||||
|
public Cartesian(double xc, double yc, double zc)
|
||||||
|
{
|
||||||
|
//formulas:
|
||||||
|
x = xc;
|
||||||
|
y = yc;
|
||||||
|
z = zc;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Updates Cartesian Values
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="c"></param>
|
||||||
|
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;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// X Coordinate
|
||||||
|
/// </summary>
|
||||||
|
public double X
|
||||||
|
{
|
||||||
|
get { return x; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if(x != value)
|
||||||
|
{
|
||||||
|
x = value;
|
||||||
|
NotifyPropertyChanged("X");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// y Coordinate
|
||||||
|
/// </summary>
|
||||||
|
public double Y
|
||||||
|
{
|
||||||
|
get { return y; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (y != value)
|
||||||
|
{
|
||||||
|
y = value;
|
||||||
|
NotifyPropertyChanged("Y");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Z Coordinate
|
||||||
|
/// </summary>
|
||||||
|
public double Z
|
||||||
|
{
|
||||||
|
get { return z; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (z != value)
|
||||||
|
{
|
||||||
|
z = value;
|
||||||
|
NotifyPropertyChanged("Z");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a Lat Long Coordinate object based on the provided Cartesian Coordinate
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="x">X</param>
|
||||||
|
/// <param name="y">Y</param>
|
||||||
|
/// <param name="z">Z</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a Lat Long Coordinate object based on the provided Cartesian Coordinate
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cart">Cartesian Coordinate</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Cartesian Default String Format
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Cartesian Formatted Coordinate String</returns>
|
||||||
|
/// <returns>Values rounded to the 8th place</returns>
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Math.Round(x,8).ToString() + " " + Math.Round(y, 8).ToString() + " " + Math.Round(z, 8).ToString();
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Property changed event
|
||||||
|
/// </summary>
|
||||||
|
public event PropertyChangedEventHandler PropertyChanged;
|
||||||
|
/// <summary>
|
||||||
|
/// Notify property changed
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="propName">Property name</param>
|
||||||
|
public void NotifyPropertyChanged(string propName)
|
||||||
|
{
|
||||||
|
if (this.PropertyChanged != null)
|
||||||
|
{
|
||||||
|
PropertyChanged(this, new PropertyChangedEventArgs(propName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
566
CoordinateSharp/Coordinate.ECEF.cs
Normal file
566
CoordinateSharp/Coordinate.ECEF.cs
Normal file
@ -0,0 +1,566 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.ComponentModel;
|
||||||
|
namespace CoordinateSharp
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Earth Centered - Earth Fixed (X,Y,Z) Coordinate
|
||||||
|
/// </summary>
|
||||||
|
[Serializable]
|
||||||
|
public class ECEF : INotifyPropertyChanged
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Create an ECEF Object
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="c">Coordinate</param>
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Create an ECEF Object
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="c">Coordinate</param>
|
||||||
|
/// <param name="height">Coordinate</param>
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Create an ECEF Object
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="xc">X</param>
|
||||||
|
/// <param name="yc">Y</param>
|
||||||
|
/// <param name="zc">Z</param>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Updates ECEF Values
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="c">Coordinate</param>
|
||||||
|
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;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Datum Equatorial Radius / Semi Major Axis
|
||||||
|
/// </summary>
|
||||||
|
public double Equatorial_Radius
|
||||||
|
{
|
||||||
|
get { return equatorial_radius; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Datum Flattening
|
||||||
|
/// </summary>
|
||||||
|
public double Inverse_Flattening
|
||||||
|
{
|
||||||
|
get { return inverse_flattening; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// X Coordinate
|
||||||
|
/// </summary>
|
||||||
|
public double X
|
||||||
|
{
|
||||||
|
get { return x; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (x != value)
|
||||||
|
{
|
||||||
|
x = value;
|
||||||
|
NotifyPropertyChanged("X");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// y Coordinate
|
||||||
|
/// </summary>
|
||||||
|
public double Y
|
||||||
|
{
|
||||||
|
get { return y; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (y != value)
|
||||||
|
{
|
||||||
|
y = value;
|
||||||
|
NotifyPropertyChanged("Y");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Z Coordinate
|
||||||
|
/// </summary>
|
||||||
|
public double Z
|
||||||
|
{
|
||||||
|
get { return z; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (z != value)
|
||||||
|
{
|
||||||
|
z = value;
|
||||||
|
NotifyPropertyChanged("Z");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// GeoDetic Height from Mean Sea Level.
|
||||||
|
/// Used for converting Lat Long / ECEF.
|
||||||
|
/// Default value is 0. Adjust as needed.
|
||||||
|
/// </summary>
|
||||||
|
public Distance GeoDetic_Height
|
||||||
|
{
|
||||||
|
get { return geodetic_height; }
|
||||||
|
internal set
|
||||||
|
{
|
||||||
|
if (geodetic_height != value)
|
||||||
|
{
|
||||||
|
geodetic_height = value;
|
||||||
|
NotifyPropertyChanged("Height");
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets GeoDetic height for ECEF conversion.
|
||||||
|
/// Recalculate ECEF Coordinate
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="c">Coordinate</param>
|
||||||
|
/// <param name="dist">Height</param>
|
||||||
|
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];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a Geodetic Coordinate object based on the provided ECEF Coordinate
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="x">X</param>
|
||||||
|
/// <param name="y">Y</param>
|
||||||
|
/// <param name="z">Z</param>
|
||||||
|
/// <returns>Coordinate</returns>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a Geodetic Coordinate object based on the provided ECEF Coordinate
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ecef">ECEF Coordinate</param>
|
||||||
|
/// <returns>Coordinate</returns>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// ECEF Default String Format
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>ECEF Formatted Coordinate String</returns>
|
||||||
|
/// <returns>Values rounded to the 3rd place</returns>
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Math.Round(x, 3).ToString() + " km, " + Math.Round(y, 3).ToString() + " km, " + Math.Round(z, 3).ToString() + " km";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Property changed event
|
||||||
|
/// </summary>
|
||||||
|
public event PropertyChangedEventHandler PropertyChanged;
|
||||||
|
/// <summary>
|
||||||
|
/// Notify property changed
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="propName">Property name</param>
|
||||||
|
public void NotifyPropertyChanged(string propName)
|
||||||
|
{
|
||||||
|
if (PropertyChanged != null)
|
||||||
|
{
|
||||||
|
PropertyChanged(this, new PropertyChangedEventArgs(propName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//CONVERSION LOGIC
|
||||||
|
/// <summary>
|
||||||
|
/// Initialize EARTH global variables based on the Datum
|
||||||
|
/// </summary>
|
||||||
|
private void WGS84()
|
||||||
|
{
|
||||||
|
double wgs84a = equatorial_radius / 1000;
|
||||||
|
double wgs84f = 1.0 / inverse_flattening;
|
||||||
|
double wgs84b = wgs84a * (1.0 - wgs84f);
|
||||||
|
|
||||||
|
EarthCon(wgs84a, wgs84b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets Earth Constants as Globals
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="a">a</param>
|
||||||
|
/// <param name="b">b</param>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Compute the radii at the geodetic latitude (degrees)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="lat">Latitude in degres</param>
|
||||||
|
/// <returns>double[]</returns>
|
||||||
|
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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Physical radius of the Earth
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="lat">Latidude in degrees</param>
|
||||||
|
/// <returns>double</returns>
|
||||||
|
private double rearth(double lat)
|
||||||
|
{
|
||||||
|
double[] rrnrm;
|
||||||
|
rrnrm = radcur(lat);
|
||||||
|
double r = rrnrm[0];
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts geocentric latitude to geodetic latitude
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="flatgc">Geocentric latitude</param>
|
||||||
|
/// <param name="altkm">Altitude in KM</param>
|
||||||
|
/// <returns>double</returns>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts geodetic latitude to geocentric latitude
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="flatgd">Geodetic latitude tp geocentric latitide</param>
|
||||||
|
/// <param name="altkm">Altitude in KM</param>
|
||||||
|
/// <returns>double</returns>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts lat / long to east, north, up vectors
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="flat">Latitude</param>
|
||||||
|
/// <param name="flon">Longitude</param>
|
||||||
|
/// <returns>Array[] of double[]</returns>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets ECEF vector in KM
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="lat">Latitude</param>
|
||||||
|
/// <param name="longi">Longitude</param>
|
||||||
|
/// <param name="altkm">Altitude in KM</param>
|
||||||
|
/// <returns>double[]</returns>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts ECEF X, Y, Z to GeoDetic Lat / Long and Height in KM
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="x"></param>
|
||||||
|
/// <param name="y"></param>
|
||||||
|
/// <param name="z"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
119
CoordinateSharp/Coordinate.EagerLoad.cs
Normal file
119
CoordinateSharp/Coordinate.EagerLoad.cs
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace CoordinateSharp
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Turn on/off eager loading of certain properties.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable]
|
||||||
|
public class EagerLoad
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Create an EagerLoad object
|
||||||
|
/// </summary>
|
||||||
|
public EagerLoad()
|
||||||
|
{
|
||||||
|
Celestial = true;
|
||||||
|
UTM_MGRS = true;
|
||||||
|
Cartesian = true;
|
||||||
|
ECEF = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create an EagerLoad object with all options on or off
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="isOn">Turns EagerLoad on or off</param>
|
||||||
|
public EagerLoad(bool isOn)
|
||||||
|
{
|
||||||
|
Celestial = isOn;
|
||||||
|
UTM_MGRS = isOn;
|
||||||
|
Cartesian = isOn;
|
||||||
|
ECEF = isOn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create an EagerLoad object with only the specified flag options turned on.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="et">EagerLoadType</param>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates an EagerLoad object. Only the specified flags will be set to EagerLoad.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="et">EagerLoadType</param>
|
||||||
|
/// <returns>EagerLoad</returns>
|
||||||
|
public static EagerLoad Create(EagerLoadType et)
|
||||||
|
{
|
||||||
|
EagerLoad el = new EagerLoad(et);
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Eager load celestial information.
|
||||||
|
/// </summary>
|
||||||
|
public bool Celestial { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Eager load UTM and MGRS information
|
||||||
|
/// </summary>
|
||||||
|
public bool UTM_MGRS { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Eager load Cartesian information
|
||||||
|
/// </summary>
|
||||||
|
public bool Cartesian { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Eager load ECEF information
|
||||||
|
/// </summary>
|
||||||
|
public bool ECEF { get; set; }
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// EagerLoad Enumerator
|
||||||
|
/// </summary>
|
||||||
|
[Serializable]
|
||||||
|
[Flags]
|
||||||
|
public enum EagerLoadType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// UTM and MGRS
|
||||||
|
/// </summary>
|
||||||
|
UTM_MGRS = 1,
|
||||||
|
/// <summary>
|
||||||
|
/// Celestial
|
||||||
|
/// </summary>
|
||||||
|
Celestial = 2,
|
||||||
|
/// <summary>
|
||||||
|
/// Cartesian
|
||||||
|
/// </summary>
|
||||||
|
Cartesian = 4,
|
||||||
|
/// <summary>
|
||||||
|
/// ECEF
|
||||||
|
/// </summary>
|
||||||
|
ECEF = 8
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
107
CoordinateSharp/Coordinate.Formatting.cs
Normal file
107
CoordinateSharp/Coordinate.Formatting.cs
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace CoordinateSharp
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Coordinate formatting options for a Coordinate object.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable]
|
||||||
|
public class CoordinateFormatOptions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Set default values with the constructor.
|
||||||
|
/// </summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Coordinate format type.
|
||||||
|
/// </summary>
|
||||||
|
public CoordinateFormatType Format { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Rounds Coordinates to the set value.
|
||||||
|
/// </summary>
|
||||||
|
public int Round { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Displays leading zeros.
|
||||||
|
/// </summary>
|
||||||
|
public bool Display_Leading_Zeros { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Display trailing zeros.
|
||||||
|
/// </summary>
|
||||||
|
public bool Display_Trailing_Zeros { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Allow symbols to display.
|
||||||
|
/// </summary>
|
||||||
|
public bool Display_Symbols { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Display degree symbols.
|
||||||
|
/// </summary>
|
||||||
|
public bool Display_Degree_Symbol { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Display minute symbols.
|
||||||
|
/// </summary>
|
||||||
|
public bool Display_Minute_Symbol { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Display secons symbol.
|
||||||
|
/// </summary>
|
||||||
|
public bool Display_Seconds_Symbol { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Display hyphens between values.
|
||||||
|
/// </summary>
|
||||||
|
public bool Display_Hyphens { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Show coordinate position first.
|
||||||
|
/// Will show last if set 'false'.
|
||||||
|
/// </summary>
|
||||||
|
public bool Position_First { get; set; }
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Coordinate Format Types.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable]
|
||||||
|
public enum CoordinateFormatType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Decimal Degree Format
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Example: N 40.456 W 75.456
|
||||||
|
/// </remarks>
|
||||||
|
Decimal_Degree,
|
||||||
|
/// <summary>
|
||||||
|
/// Decimal Degree Minutes Format
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Example: N 40º 34.552' W 70º 45.408'
|
||||||
|
/// </remarks>
|
||||||
|
Degree_Decimal_Minutes,
|
||||||
|
/// <summary>
|
||||||
|
/// Decimal Degree Minutes Format
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Example: N 40º 34" 36.552' W 70º 45" 24.408'
|
||||||
|
/// </remarks>
|
||||||
|
Degree_Minutes_Seconds,
|
||||||
|
/// <summary>
|
||||||
|
/// Decimal Format
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Example: 40.57674 -70.46574
|
||||||
|
/// </remarks>
|
||||||
|
Decimal
|
||||||
|
}
|
||||||
|
}
|
293
CoordinateSharp/Coordinate.MGRS.cs
Normal file
293
CoordinateSharp/Coordinate.MGRS.cs
Normal file
@ -0,0 +1,293 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.ComponentModel;
|
||||||
|
|
||||||
|
namespace CoordinateSharp
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Military Grid Reference System (MGRS). Uses the WGS 84 Datum.
|
||||||
|
/// Relies upon values from the UniversalTransverseMercator class
|
||||||
|
/// </summary>
|
||||||
|
[Serializable]
|
||||||
|
public class MilitaryGridReferenceSystem : INotifyPropertyChanged
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Create an MGRS object with WGS84 datum
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="latz">Lat Zone</param>
|
||||||
|
/// <param name="longz">Long Zone</param>
|
||||||
|
/// <param name="d">Digraph</param>
|
||||||
|
/// <param name="e">Easting</param>
|
||||||
|
/// <param name="n">Northing</param>
|
||||||
|
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;
|
||||||
|
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Create an MGRS object with custom datum
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="latz">Lat Zone</param>
|
||||||
|
/// <param name="longz">Long Zone</param>
|
||||||
|
/// <param name="d">Digraph</param>
|
||||||
|
/// <param name="e">Easting</param>
|
||||||
|
/// <param name="n">Northing</param>
|
||||||
|
/// <param name="rad">Equatorial Radius</param>
|
||||||
|
/// <param name="flt">Inverse Flattening</param>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// MGRS Zone Letter
|
||||||
|
/// </summary>
|
||||||
|
public string LatZone
|
||||||
|
{
|
||||||
|
get { return latZone; }
|
||||||
|
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// MGRS Zone Number
|
||||||
|
/// </summary>
|
||||||
|
public int LongZone
|
||||||
|
{
|
||||||
|
get { return longZone; }
|
||||||
|
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// MGRS Easting
|
||||||
|
/// </summary>
|
||||||
|
public double Easting
|
||||||
|
{
|
||||||
|
get { return easting; }
|
||||||
|
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// MGRS Northing
|
||||||
|
/// </summary>
|
||||||
|
public double Northing
|
||||||
|
{
|
||||||
|
get { return northing; }
|
||||||
|
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// MGRS Digraph
|
||||||
|
/// </summary>
|
||||||
|
public string Digraph
|
||||||
|
{
|
||||||
|
get { return digraph; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Is MGRS conversion within the coordinate system's accurate boundaries after conversion from Lat/Long.
|
||||||
|
/// </summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a Coordinate object from an MGRS/NATO UTM Coordinate
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mgrs">MilitaryGridReferenceSystem</param>
|
||||||
|
/// <returns>Coordinate object</returns>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// MGRS Default String Format
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>MGRS Formatted Coordinate String</returns>
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Property changed event
|
||||||
|
/// </summary>
|
||||||
|
public event PropertyChangedEventHandler PropertyChanged;
|
||||||
|
/// <summary>
|
||||||
|
/// Notify property changed
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="propName">Property name</param>
|
||||||
|
public void NotifyPropertyChanged(string propName)
|
||||||
|
{
|
||||||
|
if (PropertyChanged != null)
|
||||||
|
{
|
||||||
|
PropertyChanged(this, new PropertyChangedEventArgs(propName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
925
CoordinateSharp/Coordinate.Parser.cs
Normal file
925
CoordinateSharp/Coordinate.Parser.cs
Normal file
@ -0,0 +1,925 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
namespace CoordinateSharp
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Type of format a Coordinate parsed from.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable]
|
||||||
|
public enum Parse_Format_Type
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Coordinate was not initialized from a parser method.
|
||||||
|
/// </summary>
|
||||||
|
None,
|
||||||
|
/// <summary>
|
||||||
|
/// Signed Degree
|
||||||
|
/// DD.dddd
|
||||||
|
/// </summary>
|
||||||
|
Signed_Degree,
|
||||||
|
/// <summary>
|
||||||
|
/// Decimal Degree
|
||||||
|
/// P DD.dddd
|
||||||
|
/// </summary>
|
||||||
|
Decimal_Degree,
|
||||||
|
/// <summary>
|
||||||
|
/// Degree Decimal Minute
|
||||||
|
/// P DD MM.sss
|
||||||
|
/// </summary>
|
||||||
|
Degree_Decimal_Minute,
|
||||||
|
/// <summary>
|
||||||
|
/// Degree Minute Second
|
||||||
|
/// P DD MM SS.sss
|
||||||
|
/// </summary>
|
||||||
|
Degree_Minute_Second,
|
||||||
|
/// <summary>
|
||||||
|
/// Universal Transverse Mercator
|
||||||
|
/// </summary>
|
||||||
|
UTM,
|
||||||
|
/// <summary>
|
||||||
|
/// Military Grid Reference System
|
||||||
|
/// </summary>
|
||||||
|
MGRS,
|
||||||
|
/// <summary>
|
||||||
|
/// Spherical Cartesian
|
||||||
|
/// </summary>
|
||||||
|
Cartesian_Spherical,
|
||||||
|
/// <summary>
|
||||||
|
/// Earth Centered Earth Fixed
|
||||||
|
/// </summary>
|
||||||
|
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 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
|
||||||
|
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 };
|
||||||
|
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 };
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
private static bool TryDegreeMinuteSecond(string s, out double[] d)
|
||||||
|
{
|
||||||
|
d = null;
|
||||||
|
|
||||||
|
|
||||||
|
double deg;
|
||||||
|
double min;
|
||||||
|
double sec;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
//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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
592
CoordinateSharp/Coordinate.UTM.cs
Normal file
592
CoordinateSharp/Coordinate.UTM.cs
Normal file
@ -0,0 +1,592 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.ComponentModel;
|
||||||
|
|
||||||
|
namespace CoordinateSharp
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Universal Transverse Mercator (UTM) coordinate system. Uses the WGS 84 Datum.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable]
|
||||||
|
public class UniversalTransverseMercator : INotifyPropertyChanged
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a UniversalTransverMercator object with a WGS84 Datum.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="latz">Latitude zone</param>
|
||||||
|
/// <param name="longz">Longitude zone</param>
|
||||||
|
/// <param name="est">Easting</param>
|
||||||
|
/// <param name="nrt">Northing</param>
|
||||||
|
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."); }
|
||||||
|
|
||||||
|
latZone = latz;
|
||||||
|
longZone =longz;
|
||||||
|
easting = est;
|
||||||
|
northing = nrt;
|
||||||
|
|
||||||
|
equatorial_radius = 6378137.0;
|
||||||
|
inverse_flattening = 298.257223563;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a UniversalTransverMercator object with a custom Datum.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="latz">Latitude zone</param>
|
||||||
|
/// <param name="longz">Longitude zone</param>
|
||||||
|
/// <param name="est">Easting</param>
|
||||||
|
/// <param name="nrt">Northing</param>
|
||||||
|
/// <param name="radius">Equatorial Radius</param>
|
||||||
|
/// <param name="flaten">Inverse Flattening</param>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// UTM Zone Letter
|
||||||
|
/// </summary>
|
||||||
|
public string LatZone
|
||||||
|
{
|
||||||
|
get { return latZone; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (latZone != value)
|
||||||
|
{
|
||||||
|
latZone = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// UTM Zone Number
|
||||||
|
/// </summary>
|
||||||
|
public int LongZone
|
||||||
|
{
|
||||||
|
get { return longZone; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (longZone != value)
|
||||||
|
{
|
||||||
|
longZone = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// UTM Easting
|
||||||
|
/// </summary>
|
||||||
|
public double Easting
|
||||||
|
{
|
||||||
|
get { return easting; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (easting != value)
|
||||||
|
{
|
||||||
|
easting = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// UTM Northing
|
||||||
|
/// </summary>
|
||||||
|
public double Northing
|
||||||
|
{
|
||||||
|
get { return northing; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (northing != value)
|
||||||
|
{
|
||||||
|
northing = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Datum Equatorial Radius / Semi Major Axis
|
||||||
|
/// </summary>
|
||||||
|
public double Equatorial_Radius
|
||||||
|
{
|
||||||
|
get { return equatorial_radius; }
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Datum Flattening
|
||||||
|
/// </summary>
|
||||||
|
public double Inverse_Flattening
|
||||||
|
{
|
||||||
|
get { return inverse_flattening; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Is the UTM conversion within the coordinate system's accurate boundaries after conversion from Lat/Long.
|
||||||
|
/// </summary>
|
||||||
|
public bool WithinCoordinateSystemBounds
|
||||||
|
{
|
||||||
|
get { return withinCoordinateSystemBounds; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs a UTM object based off DD Lat/Long
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="lat">DD Latitude</param>
|
||||||
|
/// <param name="longi">DD Longitide</param>
|
||||||
|
/// <param name="c">Parent Coordinate Object</param>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs a UTM object based off DD Lat/Long
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="lat">DD Latitude</param>
|
||||||
|
/// <param name="longi">DD Longitide</param>
|
||||||
|
/// <param name="c">Parent Coordinate Object</param>
|
||||||
|
/// <param name="rad">Equatorial Radius</param>
|
||||||
|
/// <param name="flt">Flattening</param>
|
||||||
|
internal UniversalTransverseMercator(double lat, double longi, Coordinate c,double rad,double flt)
|
||||||
|
{
|
||||||
|
equatorial_radius = rad;
|
||||||
|
inverse_flattening = flt;
|
||||||
|
ToUTM(lat, longi, this);
|
||||||
|
|
||||||
|
coordinate = c;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs a UTM object based off a UTM coordinate
|
||||||
|
/// Not yet implemented
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="latz">Zone Letter</param>
|
||||||
|
/// <param name="longz">Zone Number</param>
|
||||||
|
/// <param name="e">Easting</param>
|
||||||
|
/// <param name="n">Northing</param>
|
||||||
|
/// <param name="c">Parent Coordinate Object</param>
|
||||||
|
/// <param name="rad">Equatorial Radius</param>
|
||||||
|
/// <param name="flt">Inverse Flattening</param>
|
||||||
|
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; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifies Lat zone when convert from UTM to DD Lat/Long
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="l">Zone Letter</param>
|
||||||
|
/// <returns>boolean</returns>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Assigns UTM values based of Lat/Long
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="lat">DD Latitude</param>
|
||||||
|
/// <param name="longi">DD longitude</param>
|
||||||
|
/// <param name="utm">UTM Object to modify</param>
|
||||||
|
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; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// UTM Default String Format
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>UTM Formatted Coordinate String</returns>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts UTM coordinate to Lat/Long
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="utm">utm</param>
|
||||||
|
/// <returns>Coordinate object</returns>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Property changed event
|
||||||
|
/// </summary>
|
||||||
|
public event PropertyChangedEventHandler PropertyChanged;
|
||||||
|
/// <summary>
|
||||||
|
/// Notify property changed
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="propName">Property name</param>
|
||||||
|
public void NotifyPropertyChanged(string propName)
|
||||||
|
{
|
||||||
|
if (this.PropertyChanged != null)
|
||||||
|
{
|
||||||
|
PropertyChanged(this, new PropertyChangedEventArgs(propName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
2122
CoordinateSharp/Coordinate.cs
Normal file
2122
CoordinateSharp/Coordinate.cs
Normal file
File diff suppressed because it is too large
Load Diff
86
CoordinateSharp/CoordinateSharp.csproj
Normal file
86
CoordinateSharp/CoordinateSharp.csproj
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFrameworks>net40; netstandard1.3; netstandard1.4; netstandard2.0</TargetFrameworks>
|
||||||
|
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||||
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
|
<Version>1.1.5.2</Version>
|
||||||
|
<Authors>Justin Gielski</Authors>
|
||||||
|
<Company />
|
||||||
|
<PackageProjectUrl>https://github.com/Tronald/CoordinateSharp</PackageProjectUrl>
|
||||||
|
<PackageLicenseUrl></PackageLicenseUrl>
|
||||||
|
<Copyright>Copyright 2019</Copyright>
|
||||||
|
<Description>A simple .NET standard library that is designed to assist with geographic coordinate conversions, formatting and location based celestial calculations.</Description>
|
||||||
|
<PackageReleaseNotes>Fixes issues with distance bearings reversing in certain regions.</PackageReleaseNotes>
|
||||||
|
<PackageTags>CoordinateSharp Latitude Longitude Coordinates Geography Sun Moon Solar Lunar Time MGRS UTM Julian ECEF</PackageTags>
|
||||||
|
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||||
|
<PackageIconUrl>https://raw.githubusercontent.com/Tronald/CoordinateSharp/master/ICON.png</PackageIconUrl>
|
||||||
|
<PackageId>CoordinateSharp</PackageId>
|
||||||
|
<Title>CoordinateSharp</Title>
|
||||||
|
<AssemblyVersion>1.1.5.2</AssemblyVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net40|AnyCPU'">
|
||||||
|
<DocumentationFile>bin\Release\net40\CoordinateSharp.xml</DocumentationFile>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|netstandard1.3|AnyCPU'">
|
||||||
|
<DocumentationFile>bin\Release\netstandard1.3\CoordinateSharp.xml</DocumentationFile>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|netstandard1.4|AnyCPU'">
|
||||||
|
<DocumentationFile>bin\Release\netstandard1.4\CoordinateSharp.xml</DocumentationFile>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|netstandard2.0|AnyCPU'">
|
||||||
|
<DocumentationFile>bin\Release\netstandard2.0\CoordinateSharp.xml</DocumentationFile>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net40|AnyCPU'">
|
||||||
|
<DocumentationFile>bin\Debug\net40\CoordinateSharp.xml</DocumentationFile>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|netstandard1.3|AnyCPU'">
|
||||||
|
<DocumentationFile>bin\Debug\netstandard1.3\CoordinateSharp.xml</DocumentationFile>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|netstandard1.4|AnyCPU'">
|
||||||
|
<DocumentationFile>bin\Debug\netstandard1.4\CoordinateSharp.xml</DocumentationFile>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|netstandard2.0|AnyCPU'">
|
||||||
|
<DocumentationFile>bin\Debug\netstandard2.0\CoordinateSharp.xml</DocumentationFile>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup Condition="'$(TargetFramework)' == 'net40'">
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
|
||||||
|
<PackageReference Include="System.Reflection.TypeExtensions">
|
||||||
|
<Version>4.5.0</Version>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="System.Runtime.Serialization.Formatters">
|
||||||
|
<Version>4.3.0</Version>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="System.Runtime.Serialization.Primitives">
|
||||||
|
<Version>4.3.0</Version>
|
||||||
|
</PackageReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard1.4'">
|
||||||
|
<PackageReference Include="System.Reflection.TypeExtensions">
|
||||||
|
<Version>4.5.0</Version>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="System.Runtime.Serialization.Formatters">
|
||||||
|
<Version>4.3.0</Version>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="System.Runtime.Serialization.Primitives">
|
||||||
|
<Version>4.3.0</Version>
|
||||||
|
</PackageReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard1.3'">
|
||||||
|
<PackageReference Include="System.Reflection.TypeExtensions">
|
||||||
|
<Version>4.5.0</Version>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="System.Runtime.Serialization.Formatters">
|
||||||
|
<Version>4.3.0</Version>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="System.Runtime.Serialization.Primitives">
|
||||||
|
<Version>4.3.0</Version>
|
||||||
|
</PackageReference>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
442
CoordinateSharp/Distance.cs
Normal file
442
CoordinateSharp/Distance.cs
Normal file
@ -0,0 +1,442 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
namespace CoordinateSharp
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Contains distance values between two coordinates.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable]
|
||||||
|
public class Distance
|
||||||
|
{
|
||||||
|
private double kilometers;
|
||||||
|
private double miles;
|
||||||
|
private double feet;
|
||||||
|
private double meters;
|
||||||
|
private double bearing;
|
||||||
|
private double nauticalMiles;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a distance object using Haversine (Spherical Earth).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="c1">Coordinate 1</param>
|
||||||
|
/// <param name="c2">Coordinate 2</param>
|
||||||
|
public Distance(Coordinate c1, Coordinate c2)
|
||||||
|
{
|
||||||
|
Haversine(c1, c2);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a distance object using Haversine (Spherical Earth) or Vincenty (Elliptical Earth).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="c1">Coordinate 1</param>
|
||||||
|
/// <param name="c2">Coordinate 2</param>
|
||||||
|
/// <param name="shape">Shape of earth</param>
|
||||||
|
public Distance(Coordinate c1, Coordinate c2, Shape shape)
|
||||||
|
{
|
||||||
|
if (shape == Shape.Sphere)
|
||||||
|
{
|
||||||
|
Haversine(c1, c2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Vincenty(c1, c2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes distance object based on distance in KM
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="km">Kilometers</param>
|
||||||
|
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
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Initializaes distance object based on specified distance and measurement type
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="distance">Distance</param>
|
||||||
|
/// <param name="type">Measurement type</param>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Distance in Kilometers
|
||||||
|
/// </summary>
|
||||||
|
public double Kilometers
|
||||||
|
{
|
||||||
|
get { return kilometers; }
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Distance in Statute Miles
|
||||||
|
/// </summary>
|
||||||
|
public double Miles
|
||||||
|
{
|
||||||
|
get { return miles; }
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Distance in Nautical Miles
|
||||||
|
/// </summary>
|
||||||
|
public double NauticalMiles
|
||||||
|
{
|
||||||
|
get { return nauticalMiles; }
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Distance in Meters
|
||||||
|
/// </summary>
|
||||||
|
public double Meters
|
||||||
|
{
|
||||||
|
get { return meters; }
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Distance in Feet
|
||||||
|
/// </summary>
|
||||||
|
public double Feet
|
||||||
|
{
|
||||||
|
get { return feet; }
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Initial Bearing from Coordinate 1 to Coordinate 2
|
||||||
|
/// </summary>
|
||||||
|
public double Bearing
|
||||||
|
{
|
||||||
|
get { return bearing; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Distance measurement type
|
||||||
|
/// </summary>
|
||||||
|
public enum DistanceType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Distance in Meters
|
||||||
|
/// </summary>
|
||||||
|
Meters,
|
||||||
|
/// <summary>
|
||||||
|
/// Distance in Kilometers
|
||||||
|
/// </summary>
|
||||||
|
Kilometers,
|
||||||
|
/// <summary>
|
||||||
|
/// Distance in Feet
|
||||||
|
/// </summary>
|
||||||
|
Feet,
|
||||||
|
/// <summary>
|
||||||
|
/// Distance in Statute Miles
|
||||||
|
/// </summary>
|
||||||
|
Miles,
|
||||||
|
/// <summary>
|
||||||
|
/// Distance in Nautical Miles
|
||||||
|
/// </summary>
|
||||||
|
NauticalMiles
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
internal class Distance_Assistant
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Returns new geodetic coordinate in radians
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="glat1">Latitude in Radians</param>
|
||||||
|
/// <param name="glon1">Longitude in Radians</param>
|
||||||
|
/// <param name="faz">Bearing</param>
|
||||||
|
/// <param name="s">Distance</param>
|
||||||
|
/// <param name="ellipse">Earth Ellipse Values</param>
|
||||||
|
/// <returns>double[]</returns>
|
||||||
|
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);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 };
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Returns new geodetic coordinate in radians
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="lat1">Latitude in radians</param>
|
||||||
|
/// <param name="lon1">Longitude in radians</param>
|
||||||
|
/// <param name="crs12">Bearing</param>
|
||||||
|
/// <param name="d12">Distance</param>
|
||||||
|
/// <returns>double[]</returns>
|
||||||
|
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 };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14610
CoordinateSharp/Eclipse/LunarData.cs
Normal file
14610
CoordinateSharp/Eclipse/LunarData.cs
Normal file
File diff suppressed because it is too large
Load Diff
21578
CoordinateSharp/Eclipse/SolarData.cs
Normal file
21578
CoordinateSharp/Eclipse/SolarData.cs
Normal file
File diff suppressed because it is too large
Load Diff
161
CoordinateSharp/GeoFence.cs
Normal file
161
CoordinateSharp/GeoFence.cs
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace CoordinateSharp
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Geo Fence class. It helps to check if points/coordinates are inside a polygon,
|
||||||
|
/// Next to a polyline, and counting...
|
||||||
|
/// </summary>
|
||||||
|
public class GeoFence
|
||||||
|
{
|
||||||
|
#region Fields
|
||||||
|
private List<Point> _points = new List<Point>();
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Prepare GeoFence with a list of points
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="points">List of points</param>
|
||||||
|
public GeoFence(List<Point> points)
|
||||||
|
{
|
||||||
|
_points = points;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Prepare Geofence with a list of coordinates
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="coordinates">List of coordinates</param>
|
||||||
|
public GeoFence(List<Coordinate> 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
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="point">The point to test</param>
|
||||||
|
/// <returns>bool</returns>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The function will return true if the point x,y is next the given range of
|
||||||
|
/// the polyline, or false if it is not.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="point">The point to test</param>
|
||||||
|
/// <param name="range">The range in meters</param>
|
||||||
|
/// <returns>bool</returns>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The function will return true if the point x,y is next the given range of
|
||||||
|
/// the polyline, or false if it is not.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="point">The point to test</param>
|
||||||
|
/// <param name="range">The range is a distance object</param>
|
||||||
|
/// <returns>bool</returns>
|
||||||
|
public bool IsPointInRangeOfLine(Coordinate point, Distance range)
|
||||||
|
{
|
||||||
|
if (point == null || range == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return IsPointInRangeOfLine(point, range.Meters);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This class is a help class to simplify GeoFence calculus
|
||||||
|
/// </summary>
|
||||||
|
public class Point
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initialize empty point
|
||||||
|
/// </summary>
|
||||||
|
public Point()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Initialize point with defined Latitude and Longitude
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="lat">Latitude (signed)</param>
|
||||||
|
/// <param name="lng">Longitude (signed)</param>
|
||||||
|
public Point(double lat, double lng)
|
||||||
|
{
|
||||||
|
Latitude = lat;
|
||||||
|
Longitude = lng;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// The longitude in degrees
|
||||||
|
/// </summary>
|
||||||
|
public double Longitude;
|
||||||
|
/// <summary>
|
||||||
|
/// The latitude in degrees
|
||||||
|
/// </summary>
|
||||||
|
public double Latitude;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2017 Justin Gielski
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
86
README.md
Normal file
86
README.md
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
<p align="center"><img src="https://s8.postimg.cc/y7wuenuzp/LOGO_COORDINATE_SHARP.jpg"></p>
|
||||||
|
|
||||||
|
<h2 align="center">v1.1.5.2</h2>
|
||||||
|
|
||||||
|
CoordinateSharp is a simple .NET library that is designed to assist with geographic coordinate conversions, formatting and location based celestial calculations. This library has the ability to convert various lat long formats, UTM, MGRS(NATO UTM) and Cartesian (Spherical and ECEF X, Y, Z).
|
||||||
|
|
||||||
|
<b>CAUTION:</b> v1.1.5.1 begins the depracation of certain methods in preparation of CoordinateSharp v2.1.1.1. Many user mutable items, have also been made user immutable in v1.1.5.1. This only effects properties that do not need to be set by users (i.e. Celestial properties). You should see no negative effects from this conversion if the library has been used in accordance with the [CoordinateSharp Developer Guide](https://coordinatesharp.com/DeveloperGuide).
|
||||||
|
|
||||||
|
<b>ANNOUNCEMENT:</b> v2.1.1.1 development has begun. This version is going to focus on simplifying constructors, better management of circular references and property change notification. It is also going to focus on proper assignment of user mutable vs user immutable properties. Tests will be expanded. Lastly, code examples are going to be provided in the library documentation in order to give developers a more standard documentation experience outside of the Developer Guide. This version should give the user an even simpler experience when using CoordinateSharp.
|
||||||
|
|
||||||
|
Please review our upcoming [licensing change issue](https://github.com/Tronald/CoordinateSharp/issues/93).
|
||||||
|
|
||||||
|
Change notes can be viewed [here](https://www.coordinatesharp.com/ChangeNotes)
|
||||||
|
|
||||||
|
### Like CoordinateSharp? Tell us about it!
|
||||||
|
|
||||||
|
This library was built to help other developers. Please make the time and effort worth while by [telling us what you are using it for](https://github.com/Tronald/CoordinateSharp/issues/79).
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
.NET 4.0 or .NET Standard 2.0, 1.4, 1.3 compatible runtimes.
|
||||||
|
|
||||||
|
### Installing
|
||||||
|
CoordinateSharp is available as a nuget package from [nuget.org](https://www.nuget.org/packages/CoordinateSharp/)
|
||||||
|
|
||||||
|
Alternatively, you may download the library directly [on our website](https://www.coordinatesharp.com/Download)
|
||||||
|
|
||||||
|
### Usage Example
|
||||||
|
|
||||||
|
|
||||||
|
CoordinateSharp is simple to use. In the below example we create a `Coordinate` using one of the methods below.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
//Seattle coordinates on 5 Jun 2018 @ 10:10 AM (UTC)
|
||||||
|
//Signed-Decimal Degree 47.6062, -122.3321
|
||||||
|
//Degrees Minutes Seconds N 47º 36' 22.32" W 122º 19' 55.56"
|
||||||
|
|
||||||
|
/***********************************************************/
|
||||||
|
|
||||||
|
//Initialize with signed degree (standard method)
|
||||||
|
Coordinate c = new Coordinate(47.6062, -122.3321, new DateTime(2018,6,5,10,10,0));
|
||||||
|
|
||||||
|
/***IF OTHER FORMAT IS USED SUCH AS DEGREE MINUTES SECONDS***/
|
||||||
|
|
||||||
|
//Initialize with TryParse() Method
|
||||||
|
Coordinate.TryParse("N 47º 36' 22.32\" W 122º 19' 55.56\"", new DateTime(2018,6,5,10,10,0), out c);
|
||||||
|
|
||||||
|
/****OR****/
|
||||||
|
|
||||||
|
//Initialize with Secondary Method
|
||||||
|
Coordinate c = new Coordinate();
|
||||||
|
c.Latitude = new CoordinatePart(47,36, 22.32, CoordinatePosition.N, c);
|
||||||
|
c.Longitude = new CoordinatePart(122, 19, 55.56, CoordinatePosition.W, c);
|
||||||
|
c.GeoDate = new DateTime(2018,6,5,10,10,0);
|
||||||
|
```
|
||||||
|
|
||||||
|
Once the `Coordinate` is created we have access to various formats and celestial data. Here are just a few examples.
|
||||||
|
|
||||||
|
```C#
|
||||||
|
Console.WriteLine(c); // N 47º 36' 22.32" W 122º 19' 55.56"
|
||||||
|
Console.WriteLine(c.Latitude.Seconds); // 22.32
|
||||||
|
Console.WriteLine(c.UTM); // 10T 550200mE 5272748mN
|
||||||
|
|
||||||
|
Console.WriteLine(c.CelestialInfo.SunSet); // 5-Jun-2018 4:02:00 AM
|
||||||
|
Console.WriteLine(c.CelestialInfo.MoonAltitude); // 14.4169966277874
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Abilities
|
||||||
|
|
||||||
|
* **Lat/Long formatting:** Quickly format how a coordinate is output.
|
||||||
|
* **Coordinate conversions:** Convert Lat/Long to UTM, MGRS, Cartesian (Spherical and ECEF) or vice versa.
|
||||||
|
* **Coordinate parsing:** Initialize a `Coordinate` with multiple format types using `TryParse()`.
|
||||||
|
* **Coordinate moving/shifting:** Shift coordinates using a distance and bearing, or a distance and target coordinate.
|
||||||
|
* **Location based celestial information:** Quickly determine sun set, moon rise, next solar eclipse or even zodiac signs at the input location.
|
||||||
|
* **Property change notification:** All properties automatically adjust as the `Coordinate` changes. For example, changing the `GeoDate` will cause all celestial times to recalculate. Adjusting a `Coordinate` latitudinal seconds, will retrigger all coordinate conversions and celestial data so your information is always up to date.
|
||||||
|
* **Geo-Fencing:** Define a perimeter and determine if your coordinate is within or near polylines.
|
||||||
|
|
||||||
|
### Guides
|
||||||
|
|
||||||
|
Check out the [CoordinateSharp Developer Guide](https://www.coordinatesharp.com/DeveloperGuide) for more detailed instructions on the usage and abilities of CoordinateSharp.
|
||||||
|
|
||||||
|
You may also view the [Documentation](https://www.coordinatesharp.com/Help/index.html) for a more in depth look at CoordinateSharp's structure.
|
||||||
|
|
||||||
|
<p align="center"><img src="https://s8.postimg.cc/wvf5cfpqt/LOGO_COORDINATE_SHARP_1.jpg"></p>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user