using System; using System.ComponentModel; using System.Collections.Generic; namespace CoordinateSharp { /// /// The main class for handling location based celestial information. /// /// /// This class can calculate various pieces of solar and lunar data, based on location and date /// [Serializable] public class Celestial { //When a rise or a set does not occur, the DateTime will return null /// /// Creates an empty Celestial. /// public Celestial() { astrologicalSigns = new AstrologicalSigns(); lunarEclipse = new LunarEclipse(); solarEclipse = new SolarEclipse(); CalculateCelestialTime(0, 0, new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc)); } private Celestial(bool hasCalcs) { astrologicalSigns = new AstrologicalSigns(); lunarEclipse = new LunarEclipse(); solarEclipse = new SolarEclipse(); if (hasCalcs) { CalculateCelestialTime(0, 0, new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc)); } } /// /// Creates a Celestial based on a location and specified date /// /// latitude /// longitude /// DateTime (UTC) public Celestial(double lat, double longi, DateTime geoDate) { DateTime d = new DateTime(geoDate.Year, geoDate.Month, geoDate.Day, geoDate.Hour, geoDate.Minute, geoDate.Second, DateTimeKind.Utc); astrologicalSigns = new AstrologicalSigns(); lunarEclipse = new LunarEclipse(); solarEclipse = new SolarEclipse(); CalculateCelestialTime(lat, longi, d); } /// /// Creates a Celestial based on a location and date in the provided Coordinate. /// /// Coordinate /// Celestial public static Celestial LoadCelestial(Coordinate c) { DateTime geoDate = c.GeoDate; DateTime d = new DateTime(geoDate.Year, geoDate.Month, geoDate.Day, geoDate.Hour, geoDate.Minute, geoDate.Second, DateTimeKind.Utc); Celestial cel = new Celestial(c.Latitude.ToDouble(), c.Longitude.ToDouble(), c.GeoDate); return cel; } /// /// Converts Celestial values to local times. /// /// Coordinate /// UTC offset /// public static Celestial Celestial_LocalTime(Coordinate c, double offset) { if(offset < -12 || offset > 12) { throw new ArgumentOutOfRangeException("Time offsets cannot be greater 12 or less than -12."); } //Probably need to offset initial UTC date so user can op in local //Determine best way to do this. DateTime d = c.GeoDate.AddHours(offset); //Get 3 objects for comparison Celestial cel = new Celestial(c.Latitude.ToDouble(), c.Longitude.ToDouble(), c.GeoDate); Celestial celPre = new Celestial(c.Latitude.ToDouble(), c.Longitude.ToDouble(), c.GeoDate.AddDays(-1)); Celestial celPost = new Celestial(c.Latitude.ToDouble(), c.Longitude.ToDouble(), c.GeoDate.AddDays(1)); //Slip objects for comparison. Compare with slipped date. celPre.Local_Convert(c, offset); cel.Local_Convert(c, offset); celPost.Local_Convert(c, offset); //Get SunSet int i = Determine_Slipped_Event_Index(cel.SunSet, celPre.SunSet, celPost.SunSet, d); cel.sunSet = Get_Correct_Slipped_Date(cel.SunSet, celPre.SunSet, celPost.SunSet, i); cel.AdditionalSolarTimes.CivilDusk = Get_Correct_Slipped_Date(cel.AdditionalSolarTimes.CivilDusk, celPre.AdditionalSolarTimes.CivilDusk, celPost.AdditionalSolarTimes.CivilDusk, i); cel.AdditionalSolarTimes.NauticalDusk = Get_Correct_Slipped_Date(cel.AdditionalSolarTimes.NauticalDusk, celPre.AdditionalSolarTimes.NauticalDusk, celPost.AdditionalSolarTimes.NauticalDusk, i); //Get SunRise i = Determine_Slipped_Event_Index(cel.SunRise, celPre.SunRise, celPost.SunRise, d); cel.sunRise = Get_Correct_Slipped_Date(cel.SunRise, celPre.SunRise, celPost.SunRise, i); cel.AdditionalSolarTimes.CivilDawn = Get_Correct_Slipped_Date(cel.AdditionalSolarTimes.CivilDawn, celPre.AdditionalSolarTimes.CivilDawn, celPost.AdditionalSolarTimes.CivilDawn, i); cel.AdditionalSolarTimes.NauticalDawn = Get_Correct_Slipped_Date(cel.AdditionalSolarTimes.NauticalDawn, celPre.AdditionalSolarTimes.NauticalDawn, celPost.AdditionalSolarTimes.NauticalDawn, i); //MoonRise i = Determine_Slipped_Event_Index(cel.MoonRise, celPre.MoonRise, celPost.MoonRise, d); cel.moonRise = Get_Correct_Slipped_Date(cel.MoonRise, celPre.MoonRise, celPost.MoonRise, i); //MoonSet i = Determine_Slipped_Event_Index(cel.MoonSet, celPre.MoonSet, celPost.MoonSet, d); cel.moonSet = Get_Correct_Slipped_Date(cel.MoonSet, celPre.MoonSet, celPost.MoonSet, i); //Local Conditions CelestialStatus[] cels = new CelestialStatus[] { celPre.MoonCondition,cel.MoonCondition,celPost.MoonCondition }; cel.moonCondition = Celestial.GetStatus(cel.MoonRise, cel.MoonSet, cels); cels = new CelestialStatus[] { celPre.SunCondition, cel.SunCondition, celPost.SunCondition }; cel.sunCondition = Celestial.GetStatus(cel.SunRise, cel.SunSet, cels); //Load IsUp values based on local time with populated Celestial Celestial.Calculate_Celestial_IsUp_Booleans(d, cel); return cel; } private static CelestialStatus GetStatus(DateTime? rise, DateTime? set, CelestialStatus[] cels) { if (set.HasValue && rise.HasValue) { return CelestialStatus.RiseAndSet; } if (set.HasValue && !rise.HasValue) { return CelestialStatus.NoRise; } if (!set.HasValue && rise.HasValue) { return CelestialStatus.NoSet; } for (int x=0; x < 3;x++) { if(cels[x] == CelestialStatus.DownAllDay || cels[x] == CelestialStatus.UpAllDay) { return cels[x]; } } return cels[1]; } /// /// In place time slip /// /// Coordinate /// hour offset private void Local_Convert(Coordinate c, double offset) { //Find new lunar set rise times if (MoonSet.HasValue) { moonSet = moonSet.Value.AddHours(offset); } if (MoonRise.HasValue) { moonRise = moonRise.Value.AddHours(offset); } //Perigee Perigee.ConvertTo_Local_Time(offset); //Apogee Apogee.ConvertTo_Local_Time(offset); //Eclipse LunarEclipse.ConvertTo_LocalTime(offset); ////Solar if (sunSet.HasValue) { sunSet = sunSet.Value.AddHours(offset); } if (SunRise.HasValue) { sunRise = SunRise.Value.AddHours(offset); } AdditionalSolarTimes.Convert_To_Local_Time(offset); //Eclipse SolarEclipse.ConvertTo_LocalTime(offset); SunCalc.CalculateZodiacSign(c.GeoDate.AddHours(offset), this); MoonCalc.GetMoonSign(c.GeoDate.AddHours(offset), this); } private PerigeeApogee Get_Correct_Slipped_Date(PerigeeApogee actual, PerigeeApogee pre, PerigeeApogee post, int i) { switch (i) { case 0: return pre; case 1: return actual; case 2: return post; default: return actual; } } private static DateTime? Get_Correct_Slipped_Date(DateTime? actual, DateTime? pre, DateTime? post, int i) { switch(i) { case 0: return pre; case 1: return actual; case 2: return post; default: return null; } } private static int Determine_Slipped_Event_Index(DateTime? actual, DateTime? pre, DateTime? post, DateTime d) { if (actual.HasValue) { if (actual.Value.Day != d.Day) { if (pre.HasValue) { if (pre.Value.Day == d.Day) { return 0; } } if (post.HasValue) { if (post.Value.Day == d.Day) { return 2; } } return 3; } } else { if (pre.HasValue) { if (pre.Value.Day == d.Day) { return 0; } } if (post.HasValue) { if (post.Value.Day == d.Day) { return 2; } } } return 1; } internal DateTime? sunSet; internal DateTime? sunRise; internal DateTime? moonSet; internal DateTime? moonRise; internal double sunAltitude; internal double sunAzimuth; internal double moonAltitude; internal double moonAzimuth; internal Distance moonDistance; internal CelestialStatus sunCondition; internal CelestialStatus moonCondition; internal bool isSunUp; internal bool isMoonUp; internal MoonIllum moonIllum; internal Perigee perigee; internal Apogee apogee; internal AdditionalSolarTimes additionalSolarTimes; internal AstrologicalSigns astrologicalSigns; internal SolarEclipse solarEclipse; internal LunarEclipse lunarEclipse; /// /// Sunset time. /// public DateTime? SunSet { get { return sunSet; } } /// /// Sunrise time. /// public DateTime? SunRise { get { return sunRise; } } /// /// Moonset time. /// public DateTime? MoonSet { get { return moonSet; } } /// /// Moonrise time. /// public DateTime? MoonRise { get { return moonRise; } } /// /// Sun altitude in degrees (E of N). /// public double SunAltitude { get { return sunAltitude; } } /// /// Sun azimuth in degrees (E of N). /// public double SunAzimuth { get { return sunAzimuth; } } /// /// Moon altitude in degrees (corrected for parallax and refraction). /// public double MoonAltitude { get { return moonAltitude; } } /// /// Moon azimuth in degrees (E of N). /// public double MoonAzimuth { get { return moonAzimuth; } } /// /// Estimated moon distance from the earth. /// public Distance MoonDistance { get { return moonDistance; } } /// /// Sun's Condition based on the provided date. /// public CelestialStatus SunCondition { get { return sunCondition; } } /// /// Moon's condition based on the provided date. /// public CelestialStatus MoonCondition { get { return moonCondition; } } /// /// Determine if the sun is currently up, based on sunset and sunrise time at the provided location and date. /// public bool IsSunUp{ get { return isSunUp; } } /// /// Determine if the moon is currently up, based on moonset and moonrise time at the provided location and date. /// public bool IsMoonUp { get { return isMoonUp; } } /// /// Moon ilumination details based on the provided date. /// /// /// Contains phase, phase name, fraction and angle /// public MoonIllum MoonIllum { get { return moonIllum; } } /// /// Moons perigee details based on the provided date. /// public Perigee Perigee { get { return perigee; } } /// /// Moons apogee details based on the provided date. /// public Apogee Apogee { get { return apogee; } } /// /// Additional solar event times based on the provided date and location. /// /// Contains civil and nautical dawn and dusk times. public AdditionalSolarTimes AdditionalSolarTimes { get { return additionalSolarTimes; } } /// /// Astrological signs based on the provided date. /// /// /// Contains zodiac, moon sign and moon name during full moon events /// public AstrologicalSigns AstrologicalSigns { get { return astrologicalSigns; } } /// /// Returns a SolarEclipse. /// public SolarEclipse SolarEclipse { get { return solarEclipse; } } /// /// Returns a LunarEclipse. /// public LunarEclipse LunarEclipse { get { return lunarEclipse; } } /// /// Calculates all celestial data. Coordinates will notify as changes occur /// /// Decimal format latitude /// Decimal format longitude /// Geographic DateTime internal void CalculateCelestialTime(double lat, double longi, DateTime date) { date = new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second, DateTimeKind.Utc); SunCalc.CalculateSunTime(lat, longi, date, this); MoonCalc.GetMoonTimes(date, lat, longi, this); MoonCalc.GetMoonDistance(date, this); SunCalc.CalculateZodiacSign(date, this); MoonCalc.GetMoonSign(date, this); MoonCalc.GetMoonIllumination(date, this,lat,longi); perigee = MoonCalc.GetPerigeeEvents(date); apogee = MoonCalc.GetApogeeEvents(date); Calculate_Celestial_IsUp_Booleans(date, this); } /// /// Calculate celestial data based on lat/long and date. /// /// Decimal format latitude /// Decimal format longitude /// Geographic DateTime /// Fully populated Celestial object public static Celestial CalculateCelestialTimes(double lat, double longi, DateTime date) { date = new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second, DateTimeKind.Utc); Celestial c = new Celestial(false); SunCalc.CalculateSunTime(lat, longi, date, c); MoonCalc.GetMoonTimes(date, lat, longi, c); MoonCalc.GetMoonDistance(date, c); SunCalc.CalculateZodiacSign(date, c); MoonCalc.GetMoonSign(date, c); MoonCalc.GetMoonIllumination(date, c,lat,longi); c.perigee = MoonCalc.GetPerigeeEvents(date); c.apogee = MoonCalc.GetApogeeEvents(date); Calculate_Celestial_IsUp_Booleans(date, c); return c; } /// /// Calculate sun data based on lat/long and date. /// /// latitude /// longitude /// DateTime /// Celestial (Partially Populated) public static Celestial CalculateSunData(double lat, double longi, DateTime date) { date = new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second, DateTimeKind.Utc); Celestial c = new Celestial(false); SunCalc.CalculateSunTime(lat, longi, date, c); SunCalc.CalculateZodiacSign(date, c); return c; } /// /// Calculate moon data based on lat/long and date. /// /// latitude /// longitude /// DateTime /// Celestial (Partially Populated) public static Celestial CalculateMoonData(double lat, double longi, DateTime date) { date = new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second, DateTimeKind.Utc); Celestial c = new Celestial(false); MoonCalc.GetMoonTimes(date, lat, longi, c); MoonCalc.GetMoonDistance(date, c); MoonCalc.GetMoonSign(date, c); MoonCalc.GetMoonIllumination(date, c,lat,longi); c.perigee = MoonCalc.GetPerigeeEvents(date); c.apogee = MoonCalc.GetApogeeEvents(date); return c; } /// /// Returns a List containing solar eclipse data for the century. /// Century return is based on the date passed. /// /// latitude /// longitude /// DateTime /// public static List Get_Solar_Eclipse_Table(double lat, double longi, DateTime date) { //Convert to Radians double latR = lat * Math.PI / 180; double longR = longi * Math.PI / 180; //Get solar data based on date double[] events = Eclipse.SolarData.SolarDateData_100Year(date); //Return list of solar data. return SolarEclipseCalc.CalculateSolarEclipse(date, latR, longR, events); } /// /// Returns a List containing solar eclipse data for the century. /// Century return is based on the date passed. /// /// latitude /// longitude /// DateTime /// public static List Get_Lunar_Eclipse_Table(double lat, double longi, DateTime date) { //Convert to Radians double latR = lat * Math.PI / 180; double longR = longi * Math.PI / 180; //Get solar data based on date double[] events = Eclipse.LunarData.LunarDateData_100Year(date); //Return list of solar data. return LunarEclipseCalc.CalculateLunarEclipse(date, latR, longR, events); } /// /// Set bool SunIsUp and MoonIsUp values /// /// Coordinate GeoDate /// Celestial Object private static void Calculate_Celestial_IsUp_Booleans(DateTime date, Celestial cel) { //SUN switch (cel.SunCondition) { case CelestialStatus.DownAllDay: cel.isSunUp = false; break; case CelestialStatus.UpAllDay: cel.isSunUp = true; break; case CelestialStatus.NoRise: if(date cel.SunRise) { cel.isSunUp = true; } else { cel.isSunUp = false; } break; case CelestialStatus.RiseAndSet: if (cel.SunRise < cel.SunSet) { if (date > cel.SunRise && date < cel.SunSet) { cel.isSunUp = true; } else { cel.isSunUp = false; } } else { if (date > cel.SunRise || date < cel.SunSet) { cel.isSunUp = true; } else { cel.isSunUp = false; } } break; default: //Should never be reached. If reached, previous calculations failed somewhere. break; } //MOON switch (cel.MoonCondition) { case CelestialStatus.DownAllDay: cel.isMoonUp = false; break; case CelestialStatus.UpAllDay: cel.isMoonUp = true; break; case CelestialStatus.NoRise: if (date < cel.MoonSet) { cel.isMoonUp = true; } else { cel.isMoonUp = false; } break; case CelestialStatus.NoSet: if (date > cel.MoonRise) { cel.isMoonUp = true; } else { cel.isMoonUp = false; } break; case CelestialStatus.RiseAndSet: if (cel.MoonRise < cel.MoonSet) { if (date > cel.MoonRise && date < cel.MoonSet) { cel.isMoonUp = true; } else { cel.isMoonUp = false; } } else { if (date > cel.MoonRise || date < cel.MoonSet) { cel.isMoonUp = true; } else { cel.isMoonUp = false; } } break; default: //Should never be reached. If reached, previous calculations failed somewhere. break; } } /// /// Returns Apogee object containing last and next apogee based on the specified date. /// /// DateTime /// Apogee public static Apogee GetApogees(DateTime d) { return MoonCalc.GetApogeeEvents(d); } /// /// Returns Perigee object containing last and next perigee based on the specified date. /// /// DateTime /// Perigee public static Perigee GetPerigees(DateTime d) { return MoonCalc.GetPerigeeEvents(d); } } }