From 2b0fbff159b4389a78c73e6a9fc2f816d0f279e9 Mon Sep 17 00:00:00 2001 From: Philip Schell Date: Fri, 28 Jun 2019 14:08:05 +0200 Subject: [PATCH] change poutput type to lib and make it using the right c# lang --- CoordinateSharp/Coordinate.cs | 3792 +++++++++++------------- CoordinateSharp/CoordinateSharp.csproj | 8 +- 2 files changed, 1764 insertions(+), 2036 deletions(-) diff --git a/CoordinateSharp/Coordinate.cs b/CoordinateSharp/Coordinate.cs index 74225cb..d03d76c 100644 --- a/CoordinateSharp/Coordinate.cs +++ b/CoordinateSharp/Coordinate.cs @@ -33,2090 +33,1814 @@ SOFTWARE. using System; using System.ComponentModel; -using System.Diagnostics; namespace CoordinateSharp { + /// + /// Observable class for handling all location based information. + /// This is the main class for CoordinateSharp. + /// + /// + /// All information should be pulled from this class to include celestial information + /// + [Serializable] + public class Coordinate : INotifyPropertyChanged + { /// - /// Observable class for handling all location based information. - /// This is the main class for CoordinateSharp. + /// Creates an empty Coordinate. /// /// - /// All information should be pulled from this class to include celestial information + /// Values will need to be provided to latitude/longitude CoordinateParts manually /// - [Serializable] - public class Coordinate : INotifyPropertyChanged + public Coordinate() { - /// - /// Creates an empty Coordinate. - /// - /// - /// Values will need to be provided to latitude/longitude CoordinateParts manually - /// - public Coordinate() - { - FormatOptions = new CoordinateFormatOptions(); - geoDate = new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc); - latitude = new CoordinatePart(CoordinateType.Lat); - longitude = new CoordinatePart(CoordinateType.Long); - latitude.parent = this; - longitude.parent = this; - celestialInfo = new Celestial(); - utm = new UniversalTransverseMercator(latitude.ToDouble(), longitude.ToDouble(), this); - mgrs = new MilitaryGridReferenceSystem(utm); - cartesian = new Cartesian(this); - ecef = new ECEF(this); + this.FormatOptions = new CoordinateFormatOptions(); + this.geoDate = new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc); + this.latitude = new CoordinatePart(CoordinateType.Lat); + this.longitude = new CoordinatePart(CoordinateType.Long); + this.latitude.parent = this; + this.longitude.parent = this; + this.CelestialInfo = new Celestial(); + this.UTM = new UniversalTransverseMercator(this.latitude.ToDouble(), this.longitude.ToDouble(), this); + this.MGRS = new MilitaryGridReferenceSystem(this.UTM); + this.Cartesian = new Cartesian(this); + this.ecef = new ECEF(this); - EagerLoadSettings = new EagerLoad(); + this.EagerLoadSettings = new EagerLoad(); - equatorial_radius = 6378137.0; - inverse_flattening = 298.257223563; - } - /// - /// Creates an empty Coordinate with custom datum. - /// - /// - /// Values will need to be provided to latitude/longitude CoordinateParts manually - /// - internal Coordinate(double equatorialRadius, double inverseFlattening, bool t) - { - FormatOptions = new CoordinateFormatOptions(); - geoDate = new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc); - latitude = new CoordinatePart(CoordinateType.Lat); - longitude = new CoordinatePart(CoordinateType.Long); - latitude.parent = this; - longitude.parent = this; - celestialInfo = new Celestial(); - utm = new UniversalTransverseMercator(latitude.ToDouble(), longitude.ToDouble(), this, equatorialRadius, inverseFlattening); - mgrs = new MilitaryGridReferenceSystem(utm); - cartesian = new Cartesian(this); - ecef = new ECEF(this); - - EagerLoadSettings = new EagerLoad(); - Set_Datum(equatorialRadius, inverseFlattening); - } - /// - /// Creates a populated Coordinate based on decimal (signed degrees) formated latitude and longitude. - /// - /// latitude - /// longitude - /// - /// Geodate will default to 1/1/1900 GMT until provided - /// - public Coordinate(double lat, double longi) - { - FormatOptions = new CoordinateFormatOptions(); - geoDate = new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc); - latitude = new CoordinatePart(lat, CoordinateType.Lat); - longitude = new CoordinatePart(longi, CoordinateType.Long); - latitude.parent = this; - longitude.parent = this; - celestialInfo = new Celestial(lat, longi, geoDate); - utm = new UniversalTransverseMercator(lat, longi, this); - mgrs = new MilitaryGridReferenceSystem(utm); - cartesian = new Cartesian(this); - ecef = new ECEF(this); - EagerLoadSettings = new EagerLoad(); - - equatorial_radius = 6378137.0; - inverse_flattening = 298.257223563; - } - /// - /// Creates a populated Coordinate object with an assigned GeoDate. - /// - /// latitude - /// longitude - /// DateTime (UTC) - public Coordinate(double lat, double longi, DateTime date) - { - FormatOptions = new CoordinateFormatOptions(); - latitude = new CoordinatePart(lat, CoordinateType.Lat); - longitude = new CoordinatePart(longi, CoordinateType.Long); - latitude.parent = this; - longitude.parent = this; - celestialInfo = new Celestial(lat, longi, date); - geoDate = date; - utm = new UniversalTransverseMercator(lat, longi, this); - mgrs = new MilitaryGridReferenceSystem(utm); - cartesian = new Cartesian(this); - ecef = new ECEF(this); - EagerLoadSettings = new EagerLoad(); - - equatorial_radius = 6378137.0; - inverse_flattening = 298.257223563; - } - - /// - /// Creates an empty Coordinates object with specificied eager loading options. - /// - /// - /// Values will need to be provided to latitude/longitude manually - /// - /// Eager loading options - public Coordinate(EagerLoad eagerLoad) - { - FormatOptions = new CoordinateFormatOptions(); - geoDate = geoDate = new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc); - latitude = new CoordinatePart(CoordinateType.Lat); - longitude = new CoordinatePart(CoordinateType.Long); - latitude.parent = this; - longitude.parent = this; - - if (eagerLoad.Cartesian) - { - cartesian = new Cartesian(this); - } - if (eagerLoad.Celestial) - { - celestialInfo = new Celestial(); - } - if (eagerLoad.UTM_MGRS) - { - utm = new UniversalTransverseMercator(latitude.ToDouble(), longitude.ToDouble(), this); - mgrs = new MilitaryGridReferenceSystem(utm); - } - if (eagerLoad.ECEF) - { - ecef = new ECEF(this); - } - EagerLoadSettings = eagerLoad; - - equatorial_radius = 6378137.0; - inverse_flattening = 298.257223563; - } - /// - /// Creates a populated Coordinate object with specified eager loading options. - /// - /// - /// Geodate will default to 1/1/1900 GMT until provided - /// - /// latitude - /// longitude - /// Eager loading options - public Coordinate(double lat, double longi, EagerLoad eagerLoad) - { - FormatOptions = new CoordinateFormatOptions(); - geoDate = geoDate = new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc); - latitude = new CoordinatePart(lat, CoordinateType.Lat); - longitude = new CoordinatePart(longi, CoordinateType.Long); - latitude.parent = this; - longitude.parent = this; - - if (eagerLoad.Celestial) - { - celestialInfo = new Celestial(lat, longi, geoDate); - } - if (eagerLoad.UTM_MGRS) - { - utm = new UniversalTransverseMercator(lat, longi, this); - mgrs = new MilitaryGridReferenceSystem(utm); - } - if (eagerLoad.Cartesian) - { - cartesian = new Cartesian(this); - } - if (eagerLoad.ECEF) - { - ecef = new ECEF(this); - } - - EagerLoadSettings = eagerLoad; - - equatorial_radius = 6378137.0; - inverse_flattening = 298.257223563; - } - /// - /// Creates a populated Coordinate object with specified eager load options and an assigned GeoDate. - /// - /// Decimal format latitude - /// Decimal format longitude - /// DateTime you wish to use for celestial calculation - /// Eager loading options - public Coordinate(double lat, double longi, DateTime date, EagerLoad eagerLoad) - { - FormatOptions = new CoordinateFormatOptions(); - latitude = new CoordinatePart(lat, CoordinateType.Lat); - longitude = new CoordinatePart(longi, CoordinateType.Long); - latitude.parent = this; - longitude.parent = this; - geoDate = date; - if (eagerLoad.Celestial) - { - celestialInfo = new Celestial(lat, longi, date); - } - - if (eagerLoad.UTM_MGRS) - { - utm = new UniversalTransverseMercator(lat, longi, this); - mgrs = new MilitaryGridReferenceSystem(utm); - } - if (eagerLoad.Cartesian) - { - cartesian = new Cartesian(this); - } - if (eagerLoad.ECEF) - { - ecef = new ECEF(this); - } - EagerLoadSettings = eagerLoad; - - equatorial_radius = 6378137.0; - inverse_flattening = 298.257223563; - } - - private CoordinatePart latitude; - private CoordinatePart longitude; - private UniversalTransverseMercator utm; - private MilitaryGridReferenceSystem mgrs; - private Cartesian cartesian; - private ECEF ecef; - private DateTime geoDate; - private Celestial celestialInfo; - internal double equatorial_radius; - internal double inverse_flattening; - - /// - /// Latitudinal Coordinate Part - /// - public CoordinatePart Latitude - { - get { return latitude; } - set - { - if (latitude != value) - { - if (value.Position == CoordinatesPosition.E || value.Position == CoordinatesPosition.W) - { throw new ArgumentException("Invalid Position", "Latitudinal positions cannot be set to East or West."); } - latitude = value; - latitude.parent = this; - if (EagerLoadSettings.Celestial) - { - celestialInfo.CalculateCelestialTime(Latitude.DecimalDegree, Longitude.DecimalDegree, geoDate); - } - if (longitude != null) - { - - if (EagerLoadSettings.UTM_MGRS) - { - utm = new UniversalTransverseMercator(latitude.ToDouble(), longitude.ToDouble(), this, utm.equatorial_radius, utm.inverse_flattening); - mgrs = new MilitaryGridReferenceSystem(utm); - } - if (EagerLoadSettings.Cartesian) - { - cartesian = new Cartesian(this); - } - if(EagerLoadSettings.ECEF) - { - ecef = new ECEF(this); - } - } - - } - } - } - /// - /// Longitudinal Coordinate Part - /// - public CoordinatePart Longitude - { - get { return longitude; } - set - { - if (longitude != value) - { - if (value.Position == CoordinatesPosition.N || value.Position == CoordinatesPosition.S) - { throw new ArgumentException("Invalid Position", "Longitudinal positions cannot be set to North or South."); } - longitude = value; - longitude.parent = this; - if (EagerLoadSettings.Celestial) - { - celestialInfo.CalculateCelestialTime(Latitude.DecimalDegree, Longitude.DecimalDegree, geoDate); - } - if (latitude != null) - { - if (EagerLoadSettings.UTM_MGRS) - { - utm = new UniversalTransverseMercator(latitude.ToDouble(), longitude.ToDouble(), this, utm.equatorial_radius, utm.inverse_flattening); - mgrs = new MilitaryGridReferenceSystem(utm); - } - if (EagerLoadSettings.Cartesian) - { - cartesian = new Cartesian(this); - } - if (EagerLoadSettings.ECEF) - { - ecef = new ECEF(this); - } - } - - } - } - } - /// - /// Date used to calculate celestial information - /// - /// - /// Assumes all times are in UTC - /// - public DateTime GeoDate - { - get { return geoDate; } - set - { - if (geoDate != value) - { - geoDate = value; - if (EagerLoadSettings.Celestial) - { - celestialInfo.CalculateCelestialTime(Latitude.DecimalDegree, Longitude.DecimalDegree, geoDate); - NotifyPropertyChanged("CelestialInfo"); - } - - NotifyPropertyChanged("GeoDate"); - } - } - } - /// - /// Universal Transverse Mercator Values - /// - public UniversalTransverseMercator UTM - { - get - { - return utm; - } - } - /// - /// Military Grid Reference System (NATO UTM) - /// - public MilitaryGridReferenceSystem MGRS - { - get - { - return mgrs; - } - } - /// - /// Cartesian (Based on Spherical Earth) - /// - public Cartesian Cartesian - { - get - { - return cartesian; - } - } - /// - /// Earth Centered Earth Fixed Coordinate. - /// Uses Ellipsoidal height with no geoid model included. - /// 0 = Mean Sea Level based on the provided Datum. - /// - public ECEF ECEF - { - get - { - return ecef; - } - - //Required due to GeoDetic Height - internal set - { - if (ecef != value) - { - ecef = value; - NotifyPropertyChanged("ECEF"); - } - } - } - - //PARSER INDICATOR - private Parse_Format_Type parse_Format = Parse_Format_Type.None; - /// - /// Used to determine what format the coordinate was parsed from. - /// Will equal "None" if Coordinate was not initialzed via a TryParse() method. - /// - public Parse_Format_Type Parse_Format - { - get - { - return parse_Format; - } - internal set - { - if(parse_Format!=value) - { - parse_Format = value; - NotifyPropertyChanged("Parse_Format"); - } - } - } - - /// - /// Celestial information based on the objects location and geographic UTC date. - /// - public Celestial CelestialInfo - { - get { return celestialInfo; } - } - - /// - /// Initialize celestial information (required if eager loading is turned off). - /// - public void LoadCelestialInfo() - { - celestialInfo = Celestial.LoadCelestial(this); - } - /// - /// Initialize UTM and MGRS information (required if eager loading is turned off). - /// - public void LoadUTM_MGRS_Info() - { - utm = new UniversalTransverseMercator(latitude.ToDouble(), longitude.ToDouble(), this); - mgrs = new MilitaryGridReferenceSystem(utm); - } - /// - /// Initialize cartesian information (required if eager loading is turned off). - /// - public void LoadCartesianInfo() - { - cartesian = new Cartesian(this); - } - /// - /// Initialize ECEF information (required if eager loading is turned off). - /// - public void LoadECEFInfo() - { - ecef = new ECEF(this); - } - - /// - /// Coordinate string formatting options. - /// - public CoordinateFormatOptions FormatOptions { get; set; } - /// - /// Eager loading settings. - /// - public EagerLoad EagerLoadSettings { get; set; } - - /// - /// Bindable formatted coordinate string. - /// - /// Bind to this property when MVVM patterns used - public string Display - { - get - { - return Latitude.Display + " " + Longitude.Display; - } - } - /// - /// Overridden Coordinate ToString() method. - /// - /// string (formatted). - public override string ToString() - { - string latString = latitude.ToString(); - string longSting = longitude.ToString(); - return latString + " " + longSting; - } - - /// - /// Overridden Coordinate ToString() method that accepts formatting. - /// Refer to documentation for coordinate format options. - /// - /// CoordinateFormatOptions - /// Custom formatted coordinate - public string ToString(CoordinateFormatOptions options) - { - string latString = latitude.ToString(options); - string longSting = longitude.ToString(options); - return latString + " " + longSting; - } - - /// - /// Set a custom datum for coordinate conversions and distance calculation. - /// Objects must be loaded prior to setting if EagerLoading is turned off or else the items Datum won't be set. - /// Use overload if EagerLoading options are used. - /// - /// Equatorial Radius - /// Inverse Flattening - public void Set_Datum(double radius, double flat) - { - //WGS84 - //RADIUS 6378137.0; - //FLATTENING 298.257223563; - if(utm != null) - { - utm.inverse_flattening = flat; - utm.ToUTM(Latitude.ToDouble(), Longitude.ToDouble(), utm); - mgrs = new MilitaryGridReferenceSystem(utm); - NotifyPropertyChanged("UTM"); - NotifyPropertyChanged("MGRS"); - } - if(ecef != null) - { - ecef.equatorial_radius = radius; - ecef.inverse_flattening = flat; - ecef.ToECEF(this); - NotifyPropertyChanged("ECEF"); - } - equatorial_radius = radius; - inverse_flattening = flat; - } - - /// - /// Set a custom datum for coordinate conversions and distance calculation for specified coordinate formats only. - /// Objects must be loaded prior to setting if EagerLoading is turned off. - /// - /// Equatorial Radius - /// Inverse Flattening - /// Coordinate_Datum - public void Set_Datum(double radius, double flat, Coordinate_Datum cd) - { - //WGS84 - //RADIUS 6378137.0; - //FLATTENING 298.257223563; - - if (cd.HasFlag(Coordinate_Datum.UTM_MGRS)) - { - if(utm==null || mgrs == null) { throw new NullReferenceException("UTM/MGRS objects must be loaded prior to changing the datum."); } - utm.inverse_flattening = flat; - utm.ToUTM(Latitude.ToDouble(), Longitude.ToDouble(), utm); - mgrs = new MilitaryGridReferenceSystem(utm); - NotifyPropertyChanged("UTM"); - NotifyPropertyChanged("MGRS"); - - } - if (cd.HasFlag(Coordinate_Datum.ECEF)) - { - if (ECEF==null) { throw new NullReferenceException("ECEF objects must be loaded prior to changing the datum."); } - ecef.equatorial_radius = radius; - ecef.inverse_flattening = flat; - ecef.ToECEF(this); - NotifyPropertyChanged("ECEF"); - - } - if (cd.HasFlag(Coordinate_Datum.LAT_LONG)) - { - equatorial_radius = radius; - inverse_flattening = flat; - } - } - - - /// - /// Returns a Distance object based on the current and specified coordinate (Haversine / Spherical Earth). - /// - /// Coordinate - /// Distance - public Distance Get_Distance_From_Coordinate(Coordinate c2) - { - return new Distance(this, c2); - } - /// - /// Returns a Distance object based on the current and specified coordinate and specified earth shape. - /// - /// Coordinate - /// Earth shape - /// Distance - public Distance Get_Distance_From_Coordinate(Coordinate c2, Shape shape) - { - return new Distance(this, c2, shape); - } - - /// - /// Move coordinate based on provided bearing and distance (in meters). - /// - /// Distance in meters - /// Bearing - /// Shape of earth - /// - /// The following example moves a coordinate 10km in the direction of - /// the specified bearing using ellipsoidal earth calculations. - /// - /// //N 25º 0' 0" E 25º 0' 0" - /// Coordinate c = Coordinate(25,25); - /// - /// double meters = 10000; - /// double bearing = 25; - /// - /// //Move coordinate the specified meters - /// //and direction using ellipsoidal calculations - /// c.Move(meters, bearing, Shape.Ellipsoid); - /// - /// //New Coordinate - N 25º 4' 54.517" E 24º 57' 29.189" - /// - /// - public void Move(double distance, double bearing, Shape shape) - { - //Convert to Radians for formula - double lat1 = latitude.ToRadians(); - double lon1 = longitude.ToRadians(); - double crs12 = bearing * Math.PI / 180; //Convert bearing to radians - - double[] ellipse = new double[] { equatorial_radius, inverse_flattening }; - - if (shape == Shape.Sphere) - { - double[] cd = Distance_Assistant.Direct(lat1, lon1, crs12, distance); - double lat2 = cd[0] * (180 / Math.PI); - double lon2 = cd[1] * (180 / Math.PI); - //ADJUST CORD - Latitude.DecimalDegree = lat2; - Longitude.DecimalDegree = -lon2;//v2.1.1.1 - } - else - { - double[] cde = Distance_Assistant.Direct_Ell(lat1, -lon1, crs12, distance, ellipse); // ellipse uses East negative - //Convert back from radians - double lat2 = cde[0] * (180 / Math.PI); - double lon2 = cde[1] * (180 / Math.PI); //v2.1.1.1 - //ADJUST CORD - Latitude.DecimalDegree = lat2; - Longitude.DecimalDegree = lon2; - } - } - /// - /// Move a coordinate a specified distance (in meters) towards a target coordinate. - /// - /// Target coordinate - /// Distance toward target in meters - /// Shape of earth - /// - /// The following example moves a coordinate 10km towards a target coordinate using - /// ellipsoidal earth calculations. - /// - /// //N 25º 0' 0" E 25º 0' 0" - /// Coordinate coord = Coordinate(25,25); - /// - /// //Target Coordinate - /// Coordinate target = new Coordinate(26.5, 23.2); - /// - /// double meters = 10000; - /// - /// //Move coordinate the specified meters - /// //towards target using ellipsoidal calculations - /// coord.Move(target, meters, Shape.Ellipsoid); - /// - /// //New Coordinate - N 24º 56' 21.526" E 25º 4' 23.944" - /// - /// - public void Move(Coordinate target, double distance, Shape shape) - { - Distance d = new Distance(this, target, shape); - //Convert to Radians for formula - double lat1 = latitude.ToRadians(); - double lon1 = longitude.ToRadians(); - double crs12 = d.Bearing * Math.PI / 180; //Convert bearing to radians - - double[] ellipse = new double[] { equatorial_radius, inverse_flattening }; - - if (shape == Shape.Sphere) - { - double[] cd = Distance_Assistant.Direct(lat1, lon1, crs12, distance); - double lat2 = cd[0] * (180 / Math.PI); - double lon2 = cd[1] * (180 / Math.PI); - - //ADJUST CORD - Latitude.DecimalDegree = lat2; - Longitude.DecimalDegree = -lon2; //v2.1.1.1 update - } - else - { - double[] cde = Distance_Assistant.Direct_Ell(lat1, -lon1, crs12, distance, ellipse); // ellipse uses East negative - //Convert back from radians - double lat2 = cde[0] * (180 / Math.PI); - double lon2 = cde[1] * (180 / Math.PI); // v2.1.1.1 - //ADJUST CORD - Latitude.DecimalDegree = lat2; - Longitude.DecimalDegree = lon2; - } - } - /// - /// Move coordinate based on provided bearing and distance (in meters). - /// - /// Distance - /// Bearing - /// Shape of earth - /// - /// The following example moves a coordinate 10km in the direction of - /// the specified bearing using ellipsoidal earth calculations. - /// - /// //N 25º 0' 0" E 25º 0' 0" - /// Coordinate c = Coordinate(25,25); - /// - /// Distance distance = new Distance(10, DistanceType.Kilometers); - /// double bearing = 25; - /// - /// //Move coordinate the specified distance - /// //and direction using ellipsoidal calculations - /// c.Move(distance, bearing, Shape.Ellipsoid); - /// - /// //New Coordinate - N 25º 4' 54.517" E 24º 57' 29.189" - /// - /// - public void Move(Distance distance, double bearing, Shape shape) - { - //Convert to Radians for formula - double lat1 = latitude.ToRadians(); - double lon1 = longitude.ToRadians(); - double crs12 = bearing * Math.PI / 180; //Convert bearing to radians - - double[] ellipse = new double[] { equatorial_radius, inverse_flattening }; - - if (shape == Shape.Sphere) - { - double[] cd = Distance_Assistant.Direct(lat1, lon1, crs12, distance.Meters); - double lat2 = cd[0] * (180 / Math.PI); - double lon2 = cd[1] * (180 / Math.PI); - - //ADJUST CORD - Latitude.DecimalDegree = lat2; - Longitude.DecimalDegree = -lon2; //v2.1.1.1 - } - else - { - double[] cde = Distance_Assistant.Direct_Ell(lat1, -lon1, crs12, distance.Meters, ellipse); // ellipse uses East negative - //Convert back from radians - double lat2 = cde[0] * (180 / Math.PI); - double lon2 = cde[1] * (180 / Math.PI); //v2.1.1.1 - //ADJUST CORD - Latitude.DecimalDegree = lat2; - Longitude.DecimalDegree = lon2; - } - } - /// - /// Move a coordinate a specified distance towards a target coordinate. - /// - /// Target coordinate - /// Distance toward target - /// Shape of earth - /// - /// The following example moves a coordinate 10km towards a target coordinate using - /// ellipsoidal earth calculations. - /// - /// //N 25º 0' 0" E 25º 0' 0" - /// Coordinate coord = Coordinate(25,25); - /// - /// //Target Coordinate - /// Coordinate target = new Coordinate(26.5, 23.2); - /// - /// Distance distance = new Distance(10, DistanceType.Kilometers); - /// - /// //Move coordinate the specified distance - /// //towards target using ellipsoidal calculations - /// coord.Move(target, distance, Shape.Ellipsoid); - /// - /// //New Coordinate - N 24º 56' 21.526" E 25º 4' 23.944" - /// - /// - public void Move(Coordinate target, Distance distance, Shape shape) - { - Distance d = new Distance(this, target, shape); - //Convert to Radians for formula - double lat1 = latitude.ToRadians(); - double lon1 = longitude.ToRadians(); - double crs12 = d.Bearing * Math.PI / 180; //Convert bearing to radians - - double[] ellipse = new double[] { equatorial_radius, inverse_flattening }; - - if (shape == Shape.Sphere) - { - double[] cd = Distance_Assistant.Direct(lat1, lon1, crs12, distance.Meters); - double lat2 = cd[0] * (180 / Math.PI); - double lon2 = cd[1] * (180 / Math.PI); - - //ADJUST CORD - Latitude.DecimalDegree = lat2; - Longitude.DecimalDegree = -lon2; //v2.1.1.1 update - } - else - { - double[] cde = Distance_Assistant.Direct_Ell(lat1, -lon1, crs12, distance.Meters, ellipse); - //Convert back from radians - double lat2 = cde[0] * (180 / Math.PI); - double lon2 = cde[1] * (180 / Math.PI); //v2.1.1.1 - //ADJUST CORD - Latitude.DecimalDegree = lat2; - Longitude.DecimalDegree = lon2; - } - } - - /// - /// Attempts to parse a string into a Coordinate. - /// - /// Coordinate string - /// Coordinate - /// boolean - /// - /// - /// Coordinate c; - /// if(Coordinate.TryParse("N 32.891º W 64.872º",out c)) - /// { - /// Console.WriteLine(c); //N 32º 53' 28.212" W 64º 52' 20.914" - /// } - /// - /// - public static bool TryParse(string s, out Coordinate c) - { - c = null; - if (FormatFinder.TryParse(s, CartesianType.Cartesian, out c)) - { - Parse_Format_Type pft = c.Parse_Format; - c = new Coordinate(c.Latitude.ToDouble(), c.Longitude.ToDouble()); //Reset with EagerLoad back on. - c.parse_Format = pft; - - return true; - } - return false; - } - /// - /// Attempts to parse a string into a Coordinate with specified DateTime - /// - /// Coordinate string - /// GeoDate - /// Coordinate - /// boolean - /// - /// - /// Coordinate c; - /// if(Coordinate.TryParse("N 32.891º W 64.872º", new DateTime(2018,7,7), out c)) - /// { - /// Console.WriteLine(c); //N 32º 53' 28.212" W 64º 52' 20.914" - /// } - /// - /// - public static bool TryParse(string s, DateTime geoDate, out Coordinate c) - { - c = null; - if (FormatFinder.TryParse(s, CartesianType.Cartesian, out c)) - { - Parse_Format_Type pft = c.Parse_Format; - c = new Coordinate(c.Latitude.ToDouble(), c.Longitude.ToDouble(), geoDate); //Reset with EagerLoad back on. - c.parse_Format = pft; - - return true; - } - return false; - } - /// - /// Attempts to parse a string into a Coordinate. - /// - /// Coordinate string - /// Coordinate - /// Cartesian Type - /// boolean - /// - /// - /// Coordinate c; - /// if(Coordinate.TryParse("N 32.891º W 64.872º", CartesianType.Cartesian, out c)) - /// { - /// Console.WriteLine(c); //N 32º 53' 28.212" W 64º 52' 20.914" - /// } - /// - /// - public static bool TryParse(string s, CartesianType ct, out Coordinate c) - { - c = null; - if (FormatFinder.TryParse(s, ct, out c)) - { - Parse_Format_Type pft = c.Parse_Format; - if (ct == CartesianType.ECEF) - { - Distance h = c.ecef.GeoDetic_Height; - c = new Coordinate(c.Latitude.ToDouble(), c.Longitude.ToDouble()); //Reset with EagerLoad back on. - c.ecef.Set_GeoDetic_Height(c, h); - } - else - { - c = new Coordinate(c.Latitude.ToDouble(), c.Longitude.ToDouble()); //Reset with EagerLoad back on. - } - c.parse_Format = pft; - - return true; - } - return false; - } - /// - /// Attempts to parse a string into a Coordinate with specified DateTime - /// - /// Coordinate string - /// GeoDate - /// Coordinate - /// Cartesian Type - /// boolean - /// - /// - /// Coordinate c; - /// if(Coordinate.TryParse("N 32.891º W 64.872º", new DateTime(2018,7,7), CartesianType.Cartesian, out c)) - /// { - /// Console.WriteLine(c); //N 32º 53' 28.212" W 64º 52' 20.914" - /// } - /// - /// - public static bool TryParse(string s, DateTime geoDate, CartesianType ct, out Coordinate c) - { - c = null; - if (FormatFinder.TryParse(s, ct, out c)) - { - Parse_Format_Type pft = c.Parse_Format; - if (ct == CartesianType.ECEF) - { - Distance h = c.ecef.GeoDetic_Height; - c = new Coordinate(c.Latitude.ToDouble(), c.Longitude.ToDouble(), geoDate); //Reset with EagerLoad back on. - c.ecef.Set_GeoDetic_Height(c, h); - } - else - { - c = new Coordinate(c.Latitude.ToDouble(), c.Longitude.ToDouble(), geoDate); //Reset with EagerLoad back on. - } - c.parse_Format = pft; - - return true; - } - return false; - } - - /// - /// Property changed event - /// - public event PropertyChangedEventHandler PropertyChanged; - /// - /// Notify property changed - /// - /// Property name - public void NotifyPropertyChanged(string propName) - { - switch (propName) - { - case "CelestialInfo": - if(!EagerLoadSettings.Celestial || celestialInfo == null) { return; } //Prevent Null Exceptions and calls while eagerloading is off - celestialInfo.CalculateCelestialTime(latitude.DecimalDegree, longitude.DecimalDegree, geoDate); - break; - case "UTM": - if (!EagerLoadSettings.UTM_MGRS || UTM == null) { return; } - utm.ToUTM(latitude.ToDouble(), longitude.ToDouble(), utm); - break; - case "utm": - //Adjust case and notify of change. - //Use to notify without calling ToUTM() - propName = "UTM"; - break; - case "MGRS": - if (!EagerLoadSettings.UTM_MGRS || MGRS == null) { return; } - MGRS.ToMGRS(utm); - break; - case "Cartesian": - if (!EagerLoadSettings.Cartesian || Cartesian == null) { return; } - Cartesian.ToCartesian(this); - break; - case "ECEF": - if (!EagerLoadSettings.ECEF) { return; } - ECEF.ToECEF(this); - break; - default: - break; - } - if (PropertyChanged != null) - { - PropertyChanged(this, new PropertyChangedEventArgs(propName)); - } - } + this.equatorial_radius = 6378137.0; + this.inverse_flattening = 298.257223563; } /// - /// Observable class for handling latitudinal and longitudinal coordinate parts. + /// Creates an empty Coordinate with custom datum. /// /// - /// Objects can be passed to Coordinate object Latitude and Longitude properties. + /// Values will need to be provided to latitude/longitude CoordinateParts manually /// - [Serializable] - public class CoordinatePart : INotifyPropertyChanged - { - //Defaults: - //Format: Degrees Minutes Seconds - //Rounding: Dependent upon selected format - //Leading Zeros: False - //Trailing Zeros: False - //Display Symbols: True (All Symbols display) - //Display Hyphens: False - //Position Display: First + internal Coordinate(Double equatorialRadius, Double inverseFlattening, Boolean t) + { + this.FormatOptions = new CoordinateFormatOptions(); + this.geoDate = new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc); + this.latitude = new CoordinatePart(CoordinateType.Lat); + this.longitude = new CoordinatePart(CoordinateType.Long); + this.latitude.parent = this; + this.longitude.parent = this; + this.CelestialInfo = new Celestial(); + this.UTM = new UniversalTransverseMercator(this.latitude.ToDouble(), this.longitude.ToDouble(), this, equatorialRadius, inverseFlattening); + this.MGRS = new MilitaryGridReferenceSystem(this.UTM); + this.Cartesian = new Cartesian(this); + this.ecef = new ECEF(this); - private double decimalDegree; - private double decimalMinute; - private int degrees; - private int minutes; - private double seconds; - private CoordinatesPosition position; - private CoordinateType type; + this.EagerLoadSettings = new EagerLoad(); + this.Set_Datum(equatorialRadius, inverseFlattening); + } + /// + /// Creates a populated Coordinate based on decimal (signed degrees) formated latitude and longitude. + /// + /// latitude + /// longitude + /// + /// Geodate will default to 1/1/1900 GMT until provided + /// + public Coordinate(Double lat, Double longi) + { + this.FormatOptions = new CoordinateFormatOptions(); + this.geoDate = new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc); + this.latitude = new CoordinatePart(lat, CoordinateType.Lat); + this.longitude = new CoordinatePart(longi, CoordinateType.Long); + this.latitude.parent = this; + this.longitude.parent = this; + this.CelestialInfo = new Celestial(lat, longi, this.geoDate); + this.UTM = new UniversalTransverseMercator(lat, longi, this); + this.MGRS = new MilitaryGridReferenceSystem(this.UTM); + this.Cartesian = new Cartesian(this); + this.ecef = new ECEF(this); + this.EagerLoadSettings = new EagerLoad(); - internal Coordinate parent; - /// - /// Used to determine and notify the CoordinatePart parent Coordinate object. - /// - public Coordinate Parent { get { return parent; } } + this.equatorial_radius = 6378137.0; + this.inverse_flattening = 298.257223563; + } + /// + /// Creates a populated Coordinate object with an assigned GeoDate. + /// + /// latitude + /// longitude + /// DateTime (UTC) + public Coordinate(Double lat, Double longi, DateTime date) + { + this.FormatOptions = new CoordinateFormatOptions(); + this.latitude = new CoordinatePart(lat, CoordinateType.Lat); + this.longitude = new CoordinatePart(longi, CoordinateType.Long); + this.latitude.parent = this; + this.longitude.parent = this; + this.CelestialInfo = new Celestial(lat, longi, date); + this.geoDate = date; + this.UTM = new UniversalTransverseMercator(lat, longi, this); + this.MGRS = new MilitaryGridReferenceSystem(this.UTM); + this.Cartesian = new Cartesian(this); + this.ecef = new ECEF(this); + this.EagerLoadSettings = new EagerLoad(); - /// - /// Observable decimal format coordinate. - /// - public double DecimalDegree - { - get { return decimalDegree; } - set - { - //If changing, notify the needed property changes - if (decimalDegree != value) - { - //Validate the value - if (type == CoordinateType.Lat) - { - if (value > 90) - { - throw new ArgumentOutOfRangeException("Degrees out of range", "Latitude degrees cannot be greater than 90"); - } - if (value < -90) - { - throw new ArgumentOutOfRangeException("Degrees out of range", "Latitude degrees cannot be less than -90"); - } + this.equatorial_radius = 6378137.0; + this.inverse_flattening = 298.257223563; + } - } - if (type == CoordinateType.Long) - { - if (value > 180) - { - throw new ArgumentOutOfRangeException("Degrees out of range", "Longitude degrees cannot be greater than 180"); - } - if (value < -180) - { - throw new ArgumentOutOfRangeException("Degrees out of range", "Longitude degrees cannot be less than -180"); - } + /// + /// Creates an empty Coordinates object with specificied eager loading options. + /// + /// + /// Values will need to be provided to latitude/longitude manually + /// + /// Eager loading options + public Coordinate(EagerLoad eagerLoad) + { + this.FormatOptions = new CoordinateFormatOptions(); + this.geoDate = new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc); + this.latitude = new CoordinatePart(CoordinateType.Lat); + this.longitude = new CoordinatePart(CoordinateType.Long); + this.latitude.parent = this; + this.longitude.parent = this; - } - decimalDegree = value; - - //Update Position - if ((position == CoordinatesPosition.N || position == CoordinatesPosition.E) && decimalDegree < 0) - { - if (type == CoordinateType.Lat) { position = CoordinatesPosition.S; } - else { position = CoordinatesPosition.W; } - - } - if ((position == CoordinatesPosition.W || position == CoordinatesPosition.S) && decimalDegree >= 0) - { - if (type == CoordinateType.Lat) { position = CoordinatesPosition.N; } - else { position = CoordinatesPosition.E; } - - } - //Update the Degree & Decimal Minute - double degABS = Math.Abs(decimalDegree); //Make decimalDegree positive for calculations - double degFloor = Math.Truncate(degABS); //Truncate the number leftto extract the degree - decimal f = Convert.ToDecimal(degFloor); //Convert to degree to decimal to keep precision during calculations - decimal ddm = Convert.ToDecimal(degABS) - f; //Extract decimalMinute value from decimalDegree - ddm *= 60; //Multiply by 60 to get readable decimalMinute + if (eagerLoad.Cartesian) { + this.Cartesian = new Cartesian(this); + } + if (eagerLoad.Celestial) { + this.CelestialInfo = new Celestial(); + } + if (eagerLoad.UTM_MGRS) { + this.UTM = new UniversalTransverseMercator(this.latitude.ToDouble(), this.longitude.ToDouble(), this); + this.MGRS = new MilitaryGridReferenceSystem(this.UTM); + } + if (eagerLoad.ECEF) { + this.ecef = new ECEF(this); + } + this.EagerLoadSettings = eagerLoad; - double dm = Convert.ToDouble(ddm); //Convert decimalMinutes back to double for storage - int df = Convert.ToInt32(degFloor); //Convert degrees to int for storage + this.equatorial_radius = 6378137.0; + this.inverse_flattening = 298.257223563; + } + /// + /// Creates a populated Coordinate object with specified eager loading options. + /// + /// + /// Geodate will default to 1/1/1900 GMT until provided + /// + /// latitude + /// longitude + /// Eager loading options + public Coordinate(Double lat, Double longi, EagerLoad eagerLoad) + { + this.FormatOptions = new CoordinateFormatOptions(); + this.geoDate = new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc); + this.latitude = new CoordinatePart(lat, CoordinateType.Lat); + this.longitude = new CoordinatePart(longi, CoordinateType.Long); + this.latitude.parent = this; + this.longitude.parent = this; - if (degrees != df) - { - degrees = df; - - } - if (decimalMinute != dm) - { - decimalMinute = dm; - - } - //Update Minutes Seconds - double dmFloor = Math.Floor(dm); //Get number left of decimal to grab minute value - int mF = Convert.ToInt32(dmFloor); //Convert minute to int for storage - f = Convert.ToDecimal(dmFloor); //Create a second minute value and store as decimal for precise calculation + if (eagerLoad.Celestial) { + this.CelestialInfo = new Celestial(lat, longi, this.geoDate); + } + if (eagerLoad.UTM_MGRS) { + this.UTM = new UniversalTransverseMercator(lat, longi, this); + this.MGRS = new MilitaryGridReferenceSystem(this.UTM); + } + if (eagerLoad.Cartesian) { + this.Cartesian = new Cartesian(this); + } + if (eagerLoad.ECEF) { + this.ecef = new ECEF(this); + } - decimal s = ddm - f; //Get seconds from minutes - s *= 60; //Multiply by 60 to get readable seconds - double secs = Convert.ToDouble(s); //Convert back to double for storage + this.EagerLoadSettings = eagerLoad; - if (minutes != mF) - { - minutes = mF; - - } - if (seconds != secs) - { - seconds = secs; - } - NotifyProperties(PropertyTypes.DecimalDegree); - } + this.equatorial_radius = 6378137.0; + this.inverse_flattening = 298.257223563; + } + /// + /// Creates a populated Coordinate object with specified eager load options and an assigned GeoDate. + /// + /// Decimal format latitude + /// Decimal format longitude + /// DateTime you wish to use for celestial calculation + /// Eager loading options + public Coordinate(Double lat, Double longi, DateTime date, EagerLoad eagerLoad) + { + this.FormatOptions = new CoordinateFormatOptions(); + this.latitude = new CoordinatePart(lat, CoordinateType.Lat); + this.longitude = new CoordinatePart(longi, CoordinateType.Long); + this.latitude.parent = this; + this.longitude.parent = this; + this.geoDate = date; + if (eagerLoad.Celestial) { + this.CelestialInfo = new Celestial(lat, longi, date); + } + + if (eagerLoad.UTM_MGRS) { + this.UTM = new UniversalTransverseMercator(lat, longi, this); + this.MGRS = new MilitaryGridReferenceSystem(this.UTM); + } + if (eagerLoad.Cartesian) { + this.Cartesian = new Cartesian(this); + } + if (eagerLoad.ECEF) { + this.ecef = new ECEF(this); + } + this.EagerLoadSettings = eagerLoad; + + this.equatorial_radius = 6378137.0; + this.inverse_flattening = 298.257223563; + } + + private CoordinatePart latitude; + private CoordinatePart longitude; + private ECEF ecef; + private DateTime geoDate; + internal Double equatorial_radius; + internal Double inverse_flattening; + + /// + /// Latitudinal Coordinate Part + /// + public CoordinatePart Latitude + { + get => this.latitude; + set { + if (this.latitude != value) { + if (value.Position == CoordinatesPosition.E || value.Position == CoordinatesPosition.W) { throw new ArgumentException("Invalid Position", "Latitudinal positions cannot be set to East or West."); } + this.latitude = value; + this.latitude.parent = this; + if (this.EagerLoadSettings.Celestial) { + this.CelestialInfo.CalculateCelestialTime(this.Latitude.DecimalDegree, this.Longitude.DecimalDegree, this.geoDate); + } + if (this.longitude != null) { + + if (this.EagerLoadSettings.UTM_MGRS) { + this.UTM = new UniversalTransverseMercator(this.latitude.ToDouble(), this.longitude.ToDouble(), this, this.UTM.equatorial_radius, this.UTM.inverse_flattening); + this.MGRS = new MilitaryGridReferenceSystem(this.UTM); } - } - /// - /// Observable decimal format minute. - /// - public double DecimalMinute - { - get { return decimalMinute; } - set - { - if (decimalMinute != value) - { - if (value < 0) { value *= -1; }//Adjust accidental negative input - //Validate values - - decimal dm = Math.Abs(Convert.ToDecimal(value)) / 60; - double decMin = Convert.ToDouble(dm); - if (type == CoordinateType.Lat) - { - - if (degrees + decMin > 90) { throw new ArgumentOutOfRangeException("Degrees out of range", "Latitudinal degrees cannot be greater than 90"); } - } - else - { - if (degrees + decMin > 180) { throw new ArgumentOutOfRangeException("Degrees out of range", "Longitudinal degrees cannot be greater than 180"); } - } - if (value >= 60) { throw new ArgumentOutOfRangeException("Minutes out of range", "Coordinate Minutes cannot be greater than or equal to 60"); } - if (value < 0) { throw new ArgumentOutOfRangeException("Minutes out of range", "Coordinate Minutes cannot be less than 0"); } - - - decimalMinute = value; - - - decimal decValue = Convert.ToDecimal(value); //Convert value to decimal for precision during calculation - decimal dmFloor = Math.Floor(decValue); //Extract minutes - decimal secs = decValue - dmFloor; //Extract seconds - secs *= 60; //Convert seconds to human readable format - - decimal newDM = decValue / 60; //divide decimalMinute by 60 to get storage value - decimal newDD = degrees + newDM;//Add new decimal value to the floor degree value to get new decimalDegree; - if (decimalDegree < 0) { newDD = newDD * -1; } //Restore negative if needed - - decimalDegree = Convert.ToDouble(newDD); //Convert back to double for storage - - - minutes = Convert.ToInt32(dmFloor); //Convert minutes to int for storage - - seconds = Convert.ToDouble(secs); //Convert seconds to double for storage - NotifyProperties(PropertyTypes.DecimalMinute); - } + if (this.EagerLoadSettings.Cartesian) { + this.Cartesian = new Cartesian(this); } + if (this.EagerLoadSettings.ECEF) { + this.ecef = new ECEF(this); + } + } } - /// - /// Observable coordinate degree. - /// - public int Degrees - { - get { return degrees; } - set - { - //Validate Value - if (degrees != value) - { - - if (value < 0) { value *= -1; }//Adjust accidental negative input - - if (type == CoordinateType.Lat) - { - if (value + decimalMinute /100.0 > 90) - { - throw new ArgumentOutOfRangeException("Degrees", "Latitude degrees cannot be greater than 90"); - } - } - if (type == CoordinateType.Long) - { - if (value + decimalMinute /100.0 > 180) - { - throw new ArgumentOutOfRangeException("Degrees", "Longitude degrees cannot be greater than 180"); - } - - } - - decimal f = Convert.ToDecimal(degrees); - - degrees = value; - - double degABS = Math.Abs(decimalDegree); //Make decimalDegree positive for calculations - decimal dDec = Convert.ToDecimal(degABS); //Convert to Decimal for precision during calculations - //Convert degrees to decimal to keep precision - decimal dm = dDec - f; //Extract minutes - decimal newDD = degrees + dm; //Add minutes to new degree for decimalDegree - - if (decimalDegree < 0) { newDD *= -1; } //Set negative as required - - decimalDegree = Convert.ToDouble(newDD); // Convert decimalDegree to double for storage - NotifyProperties(PropertyTypes.Degree); - } + } + } + /// + /// Longitudinal Coordinate Part + /// + public CoordinatePart Longitude + { + get => this.longitude; + set { + if (this.longitude != value) { + if (value.Position == CoordinatesPosition.N || value.Position == CoordinatesPosition.S) { throw new ArgumentException("Invalid Position", "Longitudinal positions cannot be set to North or South."); } + this.longitude = value; + this.longitude.parent = this; + if (this.EagerLoadSettings.Celestial) { + this.CelestialInfo.CalculateCelestialTime(this.Latitude.DecimalDegree, this.Longitude.DecimalDegree, this.geoDate); + } + if (this.latitude != null) { + if (this.EagerLoadSettings.UTM_MGRS) { + this.UTM = new UniversalTransverseMercator(this.latitude.ToDouble(), this.longitude.ToDouble(), this, this.UTM.equatorial_radius, this.UTM.inverse_flattening); + this.MGRS = new MilitaryGridReferenceSystem(this.UTM); } - } - /// - /// Observable coordinate minute. - /// - public int Minutes - { - get { return minutes; } - set - { - if (minutes != value) - { - if (value < 0) { value *= -1; }//Adjust accidental negative input - //Validate the minutes - decimal vMin = Convert.ToDecimal(value); - if (type == CoordinateType.Lat) - { - if (degrees + (vMin / 60) > 90) { throw new ArgumentOutOfRangeException("Degrees out of range", "Latitudinal degrees cannot be greater than 90"); } - } - else - { - if (degrees + (vMin / 60) > 180) { throw new ArgumentOutOfRangeException("Degrees out of range", "Longitudinal degrees cannot be greater than 180"); } - } - if (value >= 60) - { - throw new ArgumentOutOfRangeException("Minutes out of range", "Minutes cannot be greater than or equal to 60"); - } - if (value < 0) - { - throw new ArgumentOutOfRangeException("Minutes out of range", "Minutes cannot be less than 0"); - } - decimal minFloor = Convert.ToDecimal(minutes);//Convert decimal to minutes for calculation - decimal f = Convert.ToDecimal(degrees); //Convert to degree to keep precision during calculation - - minutes = value; - - - double degABS = Math.Abs(decimalDegree); //Make decimalDegree positive - decimal dDec = Convert.ToDecimal(degABS); //Convert to decimalDegree for precision during calucation - - decimal dm = dDec - f; //Extract minutes - dm *= 60; //Make minutes human readable - - decimal secs = dm - minFloor;//Extract Seconds - - decimal newDM = minutes + secs;//Add seconds to minutes for decimalMinute - double decMin = Convert.ToDouble(newDM); //Convert decimalMinute to double for storage - decimalMinute = decMin; //Round to correct precision - - - newDM /= 60; //Convert decimalMinute to storage format - decimal newDeg = f + newDM; //Add value to degree for decimalDegree - if (decimalDegree < 0) { newDeg *= -1; }// Set to negative as required. - decimalDegree = Convert.ToDouble(newDeg);//Convert to double and roun to correct precision for storage - NotifyProperties(PropertyTypes.Minute); - } + if (this.EagerLoadSettings.Cartesian) { + this.Cartesian = new Cartesian(this); } - } - /// - /// Observable coordinate second. - /// - public double Seconds - { - get { return seconds; } - set - { - if (value < 0) { value *= -1; }//Adjust accidental negative input - if (seconds != value) - { - //Validate Seconds - decimal vSec = Convert.ToDecimal(value); - vSec /= 60; - - decimal vMin = Convert.ToDecimal(minutes); - vMin += vSec; - vMin /= 60; - - if (type == CoordinateType.Lat) - { - if (degrees + vMin > 90) { throw new ArgumentOutOfRangeException("Degrees out of range", "Latitudinal degrees cannot be greater than 90"); } - } - else - { - if (degrees + vMin > 180) { throw new ArgumentOutOfRangeException("Degrees out of range", "Longitudinal degrees cannot be greater than 180"); } - } - if (value >= 60) - { - throw new ArgumentOutOfRangeException("Seconds out of range", "Seconds cannot be greater than or equal to 60"); - } - if (value < 0) - { - throw new ArgumentOutOfRangeException("Seconds out of range", "Seconds cannot be less than 0"); - } - seconds = value; - - - double degABS = Math.Abs(decimalDegree); //Make decimalDegree positive - double degFloor = Math.Truncate(degABS); //Truncate the number left of the decimal - decimal f = Convert.ToDecimal(degFloor); //Convert to decimal to keep precision - - decimal secs = Convert.ToDecimal(seconds); //Convert seconds to decimal for calculations - secs /= 60; //Convert to storage format - decimal dm = minutes + secs;//Add seconds to minutes for decimalMinute - double minFD = Convert.ToDouble(dm); //Convert decimalMinute for storage - decimalMinute = minFD;//Round to proper precision - - decimal nm = Convert.ToDecimal(decimalMinute) / 60;//Convert decimalMinute to decimal and divide by 60 to get storage format decimalMinute - double newDeg = degrees + Convert.ToDouble(nm);//Convert to double and add to degree for storage decimalDegree - if (decimalDegree < 0) { newDeg *= -1; }//Make negative as needed - decimalDegree = newDeg;//Update decimalDegree and round to proper precision - NotifyProperties(PropertyTypes.Second); - } + if (this.EagerLoadSettings.ECEF) { + this.ecef = new ECEF(this); } - } - /// - /// Formate coordinate part string. - /// - public string Display - { - get - { - if (parent != null) - { - return ToString(parent.FormatOptions); - } - else - { - return ToString(); - } - } - } - /// - /// Observable coordinate position. - /// - public CoordinatesPosition Position - { - get { return position; } - set - { - if (position != value) - { - if (type == CoordinateType.Long && (value == CoordinatesPosition.N || value == CoordinatesPosition.S)) - { - throw new InvalidOperationException("You cannot change a Longitudinal type coordinate into a Latitudinal"); - } - if (type == CoordinateType.Lat && (value == CoordinatesPosition.E || value == CoordinatesPosition.W)) - { - throw new InvalidOperationException("You cannot change a Latitudinal type coordinate into a Longitudinal"); - } - decimalDegree *= -1; // Change the position - position = value; - NotifyProperties(PropertyTypes.Position); - } - } - } - - /// - /// Creates an empty CoordinatePart. - /// - /// CoordinateType - /// Parent Coordinate object - [Obsolete("Method is deprecated. You no longer need to pass a Coordinate object through the constructor.")] - public CoordinatePart(CoordinateType t, Coordinate c) - { - parent = c; - type = t; - decimalDegree = 0; - degrees = 0; - minutes = 0; - seconds = 0; - if (type == CoordinateType.Lat) { position = CoordinatesPosition.N; } - else { position = CoordinatesPosition.E; } - } - /// - /// Creates a populated CoordinatePart from a decimal format part. - /// - /// Coordinate decimal value - /// Coordinate type - /// Parent Coordinate object - [Obsolete("Method is deprecated. You no longer need to pass a Coordinate object through the constructor.")] - public CoordinatePart(double value, CoordinateType t, Coordinate c) - { - parent = c; - type = t; - - if (type == CoordinateType.Long) - { - if (value > 180) { throw new ArgumentOutOfRangeException("Degrees out of range", "Longitudinal coordinate decimal cannot be greater than 180."); } - if (value < -180) { throw new ArgumentOutOfRangeException("Degrees out of range", "Longitudinal coordinate decimal cannot be less than 180."); } - if (value < 0) { position = CoordinatesPosition.W; } - else { position = CoordinatesPosition.E; } - } - else - { - if (value > 90) { throw new ArgumentOutOfRangeException("Degrees out of range", "Latitudinal coordinate decimal cannot be greater than 90."); } - if (value < -90) { throw new ArgumentOutOfRangeException("Degrees out of range", "Latitudinal coordinate decimal cannot be less than 90."); } - if (value < 0) { position = CoordinatesPosition.S; } - else { position = CoordinatesPosition.N; } - } - decimal dd = Convert.ToDecimal(value); - dd = Math.Abs(dd); - decimal ddFloor = Math.Floor(dd);//DEGREE - decimal dm = dd - ddFloor; - dm *= 60; //DECIMAL MINUTE - decimal dmFloor = Math.Floor(dm); //MINUTES - decimal sec = dm - dmFloor; - sec *= 60;//SECONDS - - - decimalDegree = value; - degrees = Convert.ToInt32(ddFloor); - minutes = Convert.ToInt32(dmFloor); - decimalMinute = Convert.ToDouble(dm); - seconds = Convert.ToDouble(sec); - } - /// - /// Creates a populated CoordinatePart object from a Degrees Minutes Seconds part. - /// - /// Degrees - /// Minutes - /// Seconds - /// Coordinate Part Position - /// Parent Coordinate - [Obsolete("Method is deprecated. You no longer need to pass a Coordinate object through the constructor.")] - public CoordinatePart(int deg, int min, double sec, CoordinatesPosition pos, Coordinate c) - { - parent = c; - if (pos == CoordinatesPosition.N || pos == CoordinatesPosition.S) { type = CoordinateType.Lat; } - else { type = CoordinateType.Long; } - - if (deg < 0) { throw new ArgumentOutOfRangeException("Degrees out of range", "Degrees cannot be less than 0."); } - if (min < 0) { throw new ArgumentOutOfRangeException("Minutes out of range", "Minutes cannot be less than 0."); } - if (sec < 0) { throw new ArgumentOutOfRangeException("Seconds out of range", "Seconds cannot be less than 0."); } - if (min >= 60) { throw new ArgumentOutOfRangeException("Minutes out of range", "Minutes cannot be greater than or equal to 60."); } - if (sec >= 60) { throw new ArgumentOutOfRangeException("Seconds out of range", "Seconds cannot be greater than or equal to 60."); } - degrees = deg; - minutes = min; - seconds = sec; - position = pos; - - decimal secD = Convert.ToDecimal(sec); - secD /= 60; //Decimal Seconds - decimal minD = Convert.ToDecimal(min); - minD += secD; //Decimal Minutes - - if (type == CoordinateType.Long) - { - if (deg + (minD / 60) > 180) { throw new ArgumentOutOfRangeException("Degrees out of range", "Longitudinal Degrees cannot be greater than 180."); } - } - else - { - if (deg + (minD / 60) > 90) { throw new ArgumentOutOfRangeException("Degrees out of range", "Latitudinal Degrees cannot be greater than 90."); } - } - decimalMinute = Convert.ToDouble(minD); - decimal dd = Convert.ToDecimal(deg) + (minD / 60); - - - if (pos == CoordinatesPosition.S || pos == CoordinatesPosition.W) - { - dd *= -1; - } - decimalDegree = Convert.ToDouble(dd); - } - /// - /// Creates a populated CoordinatePart from a Degrees Minutes Seconds part. - /// - /// Degrees - /// Decimal Minutes - /// Coordinate Part Position - /// Parent Coordinate object - [Obsolete("Method is deprecated. You no longer need to pass a Coordinate object through the constructor.")] - public CoordinatePart(int deg, double minSec, CoordinatesPosition pos, Coordinate c) - { - parent = c; - - if (pos == CoordinatesPosition.N || pos == CoordinatesPosition.S) { type = CoordinateType.Lat; } - else { type = CoordinateType.Long; } - - if (deg < 0) { throw new ArgumentOutOfRangeException("Degree out of range", "Degree cannot be less than 0."); } - if (minSec < 0) { throw new ArgumentOutOfRangeException("Minutes out of range", "Minutes cannot be less than 0."); } - - if (minSec >= 60) { throw new ArgumentOutOfRangeException("Minutes out of range", "Minutes cannot be greater than or equal to 60."); } - - if (type == CoordinateType.Lat) - { - if (deg + (minSec / 60) > 90) { throw new ArgumentOutOfRangeException("Degree out of range", "Latitudinal degrees cannot be greater than 90."); } - } - else - { - if (deg + (minSec / 60) > 180) { throw new ArgumentOutOfRangeException("Degree out of range", "Longitudinal degrees cannot be greater than 180."); } - } - degrees = deg; - decimalMinute = minSec; - position = pos; - - decimal minD = Convert.ToDecimal(minSec); - decimal minFloor = Math.Floor(minD); - minutes = Convert.ToInt32(minFloor); - decimal sec = minD - minFloor; - sec *= 60; - decimal secD = Convert.ToDecimal(sec); - seconds = Convert.ToDouble(secD); - decimal dd = deg + (minD / 60); - - if (pos == CoordinatesPosition.S || pos == CoordinatesPosition.W) - { - dd *= -1; - } - decimalDegree = Convert.ToDouble(dd); - } - - /// - /// Creates an empty CoordinatePart. - /// - /// CoordinateType - public CoordinatePart(CoordinateType t) - { - type = t; - decimalDegree = 0; - degrees = 0; - minutes = 0; - seconds = 0; - if (type == CoordinateType.Lat) { position = CoordinatesPosition.N; } - else { position = CoordinatesPosition.E; } - } - /// - /// Creates a populated CoordinatePart from a decimal format part. - /// - /// Coordinate decimal value - /// Coordinate type - public CoordinatePart(double value, CoordinateType t) - { - type = t; - - if (type == CoordinateType.Long) - { - if (value > 180) { throw new ArgumentOutOfRangeException("Degrees out of range", "Longitudinal coordinate decimal cannot be greater than 180."); } - if (value < -180) { throw new ArgumentOutOfRangeException("Degrees out of range", "Longitudinal coordinate decimal cannot be less than 180."); } - if (value < 0) { position = CoordinatesPosition.W; } - else { position = CoordinatesPosition.E; } - } - else - { - if (value > 90) { throw new ArgumentOutOfRangeException("Degrees out of range", "Latitudinal coordinate decimal cannot be greater than 90."); } - if (value < -90) { throw new ArgumentOutOfRangeException("Degrees out of range", "Latitudinal coordinate decimal cannot be less than 90."); } - if (value < 0) { position = CoordinatesPosition.S; } - else { position = CoordinatesPosition.N; } - } - decimal dd = Convert.ToDecimal(value); - dd = Math.Abs(dd); - decimal ddFloor = Math.Floor(dd);//DEGREE - decimal dm = dd - ddFloor; - dm *= 60; //DECIMAL MINUTE - decimal dmFloor = Math.Floor(dm); //MINUTES - decimal sec = dm - dmFloor; - sec *= 60;//SECONDS - - - decimalDegree = value; - degrees = Convert.ToInt32(ddFloor); - minutes = Convert.ToInt32(dmFloor); - decimalMinute = Convert.ToDouble(dm); - seconds = Convert.ToDouble(sec); - } - /// - /// Creates a populated CoordinatePart object from a Degrees Minutes Seconds part. - /// - /// Degrees - /// Minutes - /// Seconds - /// Coordinate Part Position - public CoordinatePart(int deg, int min, double sec, CoordinatesPosition pos) - { - if (pos == CoordinatesPosition.N || pos == CoordinatesPosition.S) { type = CoordinateType.Lat; } - else { type = CoordinateType.Long; } - - if (deg < 0) { throw new ArgumentOutOfRangeException("Degrees out of range", "Degrees cannot be less than 0."); } - if (min < 0) { throw new ArgumentOutOfRangeException("Minutes out of range", "Minutes cannot be less than 0."); } - if (sec < 0) { throw new ArgumentOutOfRangeException("Seconds out of range", "Seconds cannot be less than 0."); } - if (min >= 60) { throw new ArgumentOutOfRangeException("Minutes out of range", "Minutes cannot be greater than or equal to 60."); } - if (sec >= 60) { throw new ArgumentOutOfRangeException("Seconds out of range", "Seconds cannot be greater than or equal to 60."); } - degrees = deg; - minutes = min; - seconds = sec; - position = pos; - - decimal secD = Convert.ToDecimal(sec); - secD /= 60; //Decimal Seconds - decimal minD = Convert.ToDecimal(min); - minD += secD; //Decimal Minutes - - if (type == CoordinateType.Long) - { - if (deg + (minD / 60) > 180) { throw new ArgumentOutOfRangeException("Degrees out of range", "Longitudinal Degrees cannot be greater than 180."); } - } - else - { - if (deg + (minD / 60) > 90) { throw new ArgumentOutOfRangeException("Degrees out of range", "Latitudinal Degrees cannot be greater than 90."); } - } - decimalMinute = Convert.ToDouble(minD); - decimal dd = Convert.ToDecimal(deg) + (minD / 60); - - - if (pos == CoordinatesPosition.S || pos == CoordinatesPosition.W) - { - dd *= -1; - } - decimalDegree = Convert.ToDouble(dd); - } - /// - /// Creates a populated CoordinatePart from a Degrees Minutes Seconds part. - /// - /// Degrees - /// Decimal Minutes - /// Coordinate Part Position - public CoordinatePart(int deg, double minSec, CoordinatesPosition pos) - { - if (pos == CoordinatesPosition.N || pos == CoordinatesPosition.S) { type = CoordinateType.Lat; } - else { type = CoordinateType.Long; } - - if (deg < 0) { throw new ArgumentOutOfRangeException("Degree out of range", "Degree cannot be less than 0."); } - if (minSec < 0) { throw new ArgumentOutOfRangeException("Minutes out of range", "Minutes cannot be less than 0."); } - - if (minSec >= 60) { throw new ArgumentOutOfRangeException("Minutes out of range", "Minutes cannot be greater than or equal to 60."); } - - if (type == CoordinateType.Lat) - { - if (deg + (minSec / 60) > 90) { throw new ArgumentOutOfRangeException("Degree out of range", "Latitudinal degrees cannot be greater than 90."); } - } - else - { - if (deg + (minSec / 60) > 180) { throw new ArgumentOutOfRangeException("Degree out of range", "Longitudinal degrees cannot be greater than 180."); } - } - degrees = deg; - decimalMinute = minSec; - position = pos; - - decimal minD = Convert.ToDecimal(minSec); - decimal minFloor = Math.Floor(minD); - minutes = Convert.ToInt32(minFloor); - decimal sec = minD - minFloor; - sec *= 60; - decimal secD = Convert.ToDecimal(sec); - seconds = Convert.ToDouble(secD); - decimal dd = deg + (minD / 60); - - if (pos == CoordinatesPosition.S || pos == CoordinatesPosition.W) - { - dd *= -1; - } - decimalDegree = Convert.ToDouble(dd); - } - - /// - /// Signed degrees (decimal) format coordinate. - /// - /// double - public double ToDouble() - { - return decimalDegree; - } - - /// - /// Overridden Coordinate ToString() method - /// - /// Dstring - public override string ToString() - { - if(parent==null) - { - return FormatString(new CoordinateFormatOptions()); - } - return FormatString(Parent.FormatOptions); - } - - /// - /// Formatted CoordinatePart string. - /// - /// CoordinateFormatOptions - /// string (formatted) - public string ToString(CoordinateFormatOptions options) - { - return FormatString(options); - } - /// - /// String formatting logic - /// - /// CoordinateFormatOptions - /// Formatted coordinate part string - private string FormatString(CoordinateFormatOptions options) - { - ToStringType type = ToStringType.Degree_Minute_Second; - int? rounding = null; - bool lead = false; - bool trail = false; - bool hyphen = false; - bool symbols = true; - bool degreeSymbol = true; - bool minuteSymbol = true; - bool secondsSymbol = true; - bool positionFirst = true; - - #region Assign Formatting Rules - switch (options.Format) - { - case CoordinateFormatType.Degree_Minutes_Seconds: - type = ToStringType.Degree_Minute_Second; - break; - case CoordinateFormatType.Degree_Decimal_Minutes: - type = ToStringType.Degree_Decimal_Minute; - break; - case CoordinateFormatType.Decimal_Degree: - type = ToStringType.Decimal_Degree; - break; - case CoordinateFormatType.Decimal: - type = ToStringType.Decimal; - break; - default: - type = ToStringType.Degree_Minute_Second; - break; - } - rounding = options.Round; - lead = options.Display_Leading_Zeros; - trail = options.Display_Trailing_Zeros; - symbols = options.Display_Symbols; - degreeSymbol = options.Display_Degree_Symbol; - minuteSymbol = options.Display_Minute_Symbol; - secondsSymbol = options.Display_Seconds_Symbol; - hyphen = options.Display_Hyphens; - positionFirst = options.Position_First; - #endregion - - switch (type) - { - case ToStringType.Decimal_Degree: - if (rounding == null) { rounding = 6; } - return ToDecimalDegreeString(rounding.Value, lead, trail, symbols, degreeSymbol, positionFirst, hyphen); - case ToStringType.Degree_Decimal_Minute: - if (rounding == null) { rounding = 3; } - return ToDegreeDecimalMinuteString(rounding.Value, lead, trail, symbols, degreeSymbol, minuteSymbol, hyphen, positionFirst); - case ToStringType.Degree_Minute_Second: - if (rounding == null) { rounding = 3; } - return ToDegreeMinuteSecondString(rounding.Value, lead, trail, symbols, degreeSymbol, minuteSymbol, secondsSymbol, hyphen, positionFirst); - case ToStringType.Decimal: - if (rounding == null) { rounding = 9; } - double dub = ToDouble(); - dub = Math.Round(dub, rounding.Value); - string lt = Leading_Trailing_Format(lead, trail, rounding.Value, Position); - return string.Format(lt, dub); - } - - return string.Empty; - } - //DMS Coordinate Format - private string ToDegreeMinuteSecondString(int rounding, bool lead, bool trail, bool symbols, bool degreeSymbol, bool minuteSymbol, bool secondSymbol, bool hyphen, bool positionFirst) - { - - string leadString = Leading_Trailing_Format(lead, false, rounding, Position); - string d = string.Format(leadString, Degrees); // Degree String - string minute; - if (lead) { minute = string.Format("{0:00}", Minutes); } - else { minute = Minutes.ToString(); } - string leadTrail = Leading_Trailing_Format(lead, trail, rounding); - - double sc = Math.Round(Seconds, rounding); - string second = string.Format(leadTrail, sc); - string hs = " "; - string ds = ""; - string ms = ""; - string ss = ""; - if (symbols) - { - if (degreeSymbol) { ds = "º"; } - if (minuteSymbol) { ms = "'"; } - if (secondSymbol) { ss = "\""; } - } - if (hyphen) { hs = "-"; } - - if (positionFirst) { return Position.ToString() + hs + d + ds + hs + minute + ms + hs + second + ss; } - else { return d + ds + hs + minute + ms + hs + second + ss + hs + Position.ToString(); } - } - //DDM Coordinate Format - private string ToDegreeDecimalMinuteString(int rounding, bool lead, bool trail, bool symbols, bool degreeSymbol, bool minuteSymbol, bool hyphen, bool positionFirst) - { - string leadString = "{0:0"; - if (lead) - { - if (Position == CoordinatesPosition.E || Position == CoordinatesPosition.W) - { - leadString += "00"; - } - else - { - leadString += "0"; - } - } - leadString += "}"; - string d = string.Format(leadString, Degrees); // Degree String - - string leadTrail = "{0:0"; - if (lead) - { - leadTrail += "0"; - } - leadTrail += "."; - if (trail) - { - for (int i = 0; i < rounding; i++) - { - leadTrail += "0"; - } - } - else - { - leadTrail += "#########"; - } - leadTrail += "}"; - - double ns = Seconds / 60; - double c = Math.Round(Minutes + ns, rounding); - if(c == 60 && Degrees+1 <91) { c = 0;d = string.Format(leadString, Degrees + 1); }//Adjust for rounded maxed out Seconds. will Convert 42 60.0 to 43 - string ms = string.Format(leadTrail, c); - string hs = " "; - string ds = ""; - string ss = ""; - if (symbols) - { - if (degreeSymbol) { ds = "º"; } - if (minuteSymbol) { ss = "'"; } - } - if (hyphen) { hs = "-"; } - - if (positionFirst) { return Position.ToString() + hs + d + ds + hs + ms + ss; } - else { return d + ds + hs + ms + ss + hs + Position.ToString(); } + } } - ////DD Coordinate Format - private string ToDecimalDegreeString(int rounding, bool lead, bool trail, bool symbols, bool degreeSymbol, bool positionFirst, bool hyphen) - { - string degreeS = ""; - string hyph = " "; - if (degreeSymbol) { degreeS = "º"; } - if (!symbols) { degreeS = ""; } - if (hyphen) { hyph = "-"; } - - string leadTrail = "{0:0"; - if (lead) - { - if (Position == CoordinatesPosition.E || Position == CoordinatesPosition.W) - { - leadTrail += "00"; - } - else - { - leadTrail += "0"; - } - } - leadTrail += "."; - if (trail) - { - for (int i = 0; i < rounding; i++) - { - leadTrail += "0"; - } - } - else - { - leadTrail += "#########"; - } - leadTrail += "}"; - - double result = (Degrees) + (Convert.ToDouble(Minutes)) / 60 + (Convert.ToDouble(Seconds)) / 3600; - result = Math.Round(result, rounding); - string d = string.Format(leadTrail, Math.Abs(result)); - if (positionFirst) { return Position.ToString() + hyph + d + degreeS; } - else { return d + degreeS + hyph + Position.ToString(); } + } + } + /// + /// Date used to calculate celestial information + /// + /// + /// Assumes all times are in UTC + /// + public DateTime GeoDate + { + get => this.geoDate; + set { + if (this.geoDate != value) { + this.geoDate = value; + if (this.EagerLoadSettings.Celestial) { + this.CelestialInfo.CalculateCelestialTime(this.Latitude.DecimalDegree, this.Longitude.DecimalDegree, this.geoDate); + this.NotifyPropertyChanged("CelestialInfo"); + } + this.NotifyPropertyChanged("GeoDate"); } + } + } + /// + /// Universal Transverse Mercator Values + /// + public UniversalTransverseMercator UTM { get; private set; } + /// + /// Military Grid Reference System (NATO UTM) + /// + public MilitaryGridReferenceSystem MGRS { get; private set; } + /// + /// Cartesian (Based on Spherical Earth) + /// + public Cartesian Cartesian { get; private set; } + /// + /// Earth Centered Earth Fixed Coordinate. + /// Uses Ellipsoidal height with no geoid model included. + /// 0 = Mean Sea Level based on the provided Datum. + /// + public ECEF ECEF + { + get => this.ecef; - private string Leading_Trailing_Format(bool isLead, bool isTrail, int rounding, CoordinatesPosition? p = null) - { - string leadString = "{0:0"; - if (isLead) - { - if (p != null) - { - if (p.Value == CoordinatesPosition.W || p.Value == CoordinatesPosition.E) - { - leadString += "00"; - } - } - else - { - leadString += "0"; - } + //Required due to GeoDetic Height + internal set { + if (this.ecef != value) { + this.ecef = value; + this.NotifyPropertyChanged("ECEF"); + } + } + } + + //PARSER INDICATOR + private Parse_Format_Type parse_Format = Parse_Format_Type.None; + /// + /// Used to determine what format the coordinate was parsed from. + /// Will equal "None" if Coordinate was not initialzed via a TryParse() method. + /// + public Parse_Format_Type Parse_Format + { + get => this.parse_Format; + internal set { + if (this.parse_Format != value) { + this.parse_Format = value; + this.NotifyPropertyChanged("Parse_Format"); + } + } + } + + /// + /// Celestial information based on the objects location and geographic UTC date. + /// + public Celestial CelestialInfo { get; private set; } + + /// + /// Initialize celestial information (required if eager loading is turned off). + /// + public void LoadCelestialInfo() => this.CelestialInfo = Celestial.LoadCelestial(this); + /// + /// Initialize UTM and MGRS information (required if eager loading is turned off). + /// + public void LoadUTM_MGRS_Info() + { + this.UTM = new UniversalTransverseMercator(this.latitude.ToDouble(), this.longitude.ToDouble(), this); + this.MGRS = new MilitaryGridReferenceSystem(this.UTM); + } + /// + /// Initialize cartesian information (required if eager loading is turned off). + /// + public void LoadCartesianInfo() => this.Cartesian = new Cartesian(this); + /// + /// Initialize ECEF information (required if eager loading is turned off). + /// + public void LoadECEFInfo() => this.ecef = new ECEF(this); + + /// + /// Coordinate string formatting options. + /// + public CoordinateFormatOptions FormatOptions { get; set; } + /// + /// Eager loading settings. + /// + public EagerLoad EagerLoadSettings { get; set; } + + /// + /// Bindable formatted coordinate string. + /// + /// Bind to this property when MVVM patterns used + public String Display => this.Latitude.Display + " " + this.Longitude.Display; + /// + /// Overridden Coordinate ToString() method. + /// + /// string (formatted). + public override String ToString() + { + String latString = this.latitude.ToString(); + String longSting = this.longitude.ToString(); + return latString + " " + longSting; + } + + /// + /// Overridden Coordinate ToString() method that accepts formatting. + /// Refer to documentation for coordinate format options. + /// + /// CoordinateFormatOptions + /// Custom formatted coordinate + public String ToString(CoordinateFormatOptions options) + { + String latString = this.latitude.ToString(options); + String longSting = this.longitude.ToString(options); + return latString + " " + longSting; + } + + /// + /// Set a custom datum for coordinate conversions and distance calculation. + /// Objects must be loaded prior to setting if EagerLoading is turned off or else the items Datum won't be set. + /// Use overload if EagerLoading options are used. + /// + /// Equatorial Radius + /// Inverse Flattening + public void Set_Datum(Double radius, Double flat) + { + //WGS84 + //RADIUS 6378137.0; + //FLATTENING 298.257223563; + if (this.UTM != null) { + this.UTM.inverse_flattening = flat; + this.UTM.ToUTM(this.Latitude.ToDouble(), this.Longitude.ToDouble(), this.UTM); + this.MGRS = new MilitaryGridReferenceSystem(this.UTM); + this.NotifyPropertyChanged("UTM"); + this.NotifyPropertyChanged("MGRS"); + } + if (this.ecef != null) { + this.ecef.equatorial_radius = radius; + this.ecef.inverse_flattening = flat; + this.ecef.ToECEF(this); + this.NotifyPropertyChanged("ECEF"); + } + this.equatorial_radius = radius; + this.inverse_flattening = flat; + } + + /// + /// Set a custom datum for coordinate conversions and distance calculation for specified coordinate formats only. + /// Objects must be loaded prior to setting if EagerLoading is turned off. + /// + /// Equatorial Radius + /// Inverse Flattening + /// Coordinate_Datum + public void Set_Datum(Double radius, Double flat, Coordinate_Datum cd) + { + //WGS84 + //RADIUS 6378137.0; + //FLATTENING 298.257223563; + + if (cd.HasFlag(Coordinate_Datum.UTM_MGRS)) { + if (this.UTM == null || this.MGRS == null) { throw new NullReferenceException("UTM/MGRS objects must be loaded prior to changing the datum."); } + this.UTM.inverse_flattening = flat; + this.UTM.ToUTM(this.Latitude.ToDouble(), this.Longitude.ToDouble(), this.UTM); + this.MGRS = new MilitaryGridReferenceSystem(this.UTM); + this.NotifyPropertyChanged("UTM"); + this.NotifyPropertyChanged("MGRS"); + + } + if (cd.HasFlag(Coordinate_Datum.ECEF)) { + if (this.ECEF == null) { throw new NullReferenceException("ECEF objects must be loaded prior to changing the datum."); } + this.ecef.equatorial_radius = radius; + this.ecef.inverse_flattening = flat; + this.ecef.ToECEF(this); + this.NotifyPropertyChanged("ECEF"); + + } + if (cd.HasFlag(Coordinate_Datum.LAT_LONG)) { + this.equatorial_radius = radius; + this.inverse_flattening = flat; + } + } + + + /// + /// Returns a Distance object based on the current and specified coordinate (Haversine / Spherical Earth). + /// + /// Coordinate + /// Distance + public Distance Get_Distance_From_Coordinate(Coordinate c2) => new Distance(this, c2); + /// + /// Returns a Distance object based on the current and specified coordinate and specified earth shape. + /// + /// Coordinate + /// Earth shape + /// Distance + public Distance Get_Distance_From_Coordinate(Coordinate c2, Shape shape) => new Distance(this, c2, shape); + + /// + /// Move coordinate based on provided bearing and distance (in meters). + /// + /// Distance in meters + /// Bearing + /// Shape of earth + /// + /// The following example moves a coordinate 10km in the direction of + /// the specified bearing using ellipsoidal earth calculations. + /// + /// //N 25º 0' 0" E 25º 0' 0" + /// Coordinate c = Coordinate(25,25); + /// + /// double meters = 10000; + /// double bearing = 25; + /// + /// //Move coordinate the specified meters + /// //and direction using ellipsoidal calculations + /// c.Move(meters, bearing, Shape.Ellipsoid); + /// + /// //New Coordinate - N 25º 4' 54.517" E 24º 57' 29.189" + /// + /// + public void Move(Double distance, Double bearing, Shape shape) + { + //Convert to Radians for formula + Double lat1 = this.latitude.ToRadians(); + Double lon1 = this.longitude.ToRadians(); + Double crs12 = bearing * Math.PI / 180; //Convert bearing to radians + + Double[] ellipse = new Double[] { this.equatorial_radius, this.inverse_flattening }; + + if (shape == Shape.Sphere) { + Double[] cd = Distance_Assistant.Direct(lat1, lon1, crs12, distance); + Double lat2 = cd[0] * (180 / Math.PI); + Double lon2 = cd[1] * (180 / Math.PI); + //ADJUST CORD + this.Latitude.DecimalDegree = lat2; + this.Longitude.DecimalDegree = -lon2;//v2.1.1.1 + } else { + Double[] cde = Distance_Assistant.Direct_Ell(lat1, -lon1, crs12, distance, ellipse); // ellipse uses East negative + //Convert back from radians + Double lat2 = cde[0] * (180 / Math.PI); + Double lon2 = cde[1] * (180 / Math.PI); //v2.1.1.1 + //ADJUST CORD + this.Latitude.DecimalDegree = lat2; + this.Longitude.DecimalDegree = lon2; + } + } + /// + /// Move a coordinate a specified distance (in meters) towards a target coordinate. + /// + /// Target coordinate + /// Distance toward target in meters + /// Shape of earth + /// + /// The following example moves a coordinate 10km towards a target coordinate using + /// ellipsoidal earth calculations. + /// + /// //N 25º 0' 0" E 25º 0' 0" + /// Coordinate coord = Coordinate(25,25); + /// + /// //Target Coordinate + /// Coordinate target = new Coordinate(26.5, 23.2); + /// + /// double meters = 10000; + /// + /// //Move coordinate the specified meters + /// //towards target using ellipsoidal calculations + /// coord.Move(target, meters, Shape.Ellipsoid); + /// + /// //New Coordinate - N 24º 56' 21.526" E 25º 4' 23.944" + /// + /// + public void Move(Coordinate target, Double distance, Shape shape) + { + Distance d = new Distance(this, target, shape); + //Convert to Radians for formula + Double lat1 = this.latitude.ToRadians(); + Double lon1 = this.longitude.ToRadians(); + Double crs12 = d.Bearing * Math.PI / 180; //Convert bearing to radians + + Double[] ellipse = new Double[] { this.equatorial_radius, this.inverse_flattening }; + + if (shape == Shape.Sphere) { + Double[] cd = Distance_Assistant.Direct(lat1, lon1, crs12, distance); + Double lat2 = cd[0] * (180 / Math.PI); + Double lon2 = cd[1] * (180 / Math.PI); + + //ADJUST CORD + this.Latitude.DecimalDegree = lat2; + this.Longitude.DecimalDegree = -lon2; //v2.1.1.1 update + } else { + Double[] cde = Distance_Assistant.Direct_Ell(lat1, -lon1, crs12, distance, ellipse); // ellipse uses East negative + //Convert back from radians + Double lat2 = cde[0] * (180 / Math.PI); + Double lon2 = cde[1] * (180 / Math.PI); // v2.1.1.1 + //ADJUST CORD + this.Latitude.DecimalDegree = lat2; + this.Longitude.DecimalDegree = lon2; + } + } + /// + /// Move coordinate based on provided bearing and distance (in meters). + /// + /// Distance + /// Bearing + /// Shape of earth + /// + /// The following example moves a coordinate 10km in the direction of + /// the specified bearing using ellipsoidal earth calculations. + /// + /// //N 25º 0' 0" E 25º 0' 0" + /// Coordinate c = Coordinate(25,25); + /// + /// Distance distance = new Distance(10, DistanceType.Kilometers); + /// double bearing = 25; + /// + /// //Move coordinate the specified distance + /// //and direction using ellipsoidal calculations + /// c.Move(distance, bearing, Shape.Ellipsoid); + /// + /// //New Coordinate - N 25º 4' 54.517" E 24º 57' 29.189" + /// + /// + public void Move(Distance distance, Double bearing, Shape shape) + { + //Convert to Radians for formula + Double lat1 = this.latitude.ToRadians(); + Double lon1 = this.longitude.ToRadians(); + Double crs12 = bearing * Math.PI / 180; //Convert bearing to radians + + Double[] ellipse = new Double[] { this.equatorial_radius, this.inverse_flattening }; + + if (shape == Shape.Sphere) { + Double[] cd = Distance_Assistant.Direct(lat1, lon1, crs12, distance.Meters); + Double lat2 = cd[0] * (180 / Math.PI); + Double lon2 = cd[1] * (180 / Math.PI); + + //ADJUST CORD + this.Latitude.DecimalDegree = lat2; + this.Longitude.DecimalDegree = -lon2; //v2.1.1.1 + } else { + Double[] cde = Distance_Assistant.Direct_Ell(lat1, -lon1, crs12, distance.Meters, ellipse); // ellipse uses East negative + //Convert back from radians + Double lat2 = cde[0] * (180 / Math.PI); + Double lon2 = cde[1] * (180 / Math.PI); //v2.1.1.1 + //ADJUST CORD + this.Latitude.DecimalDegree = lat2; + this.Longitude.DecimalDegree = lon2; + } + } + /// + /// Move a coordinate a specified distance towards a target coordinate. + /// + /// Target coordinate + /// Distance toward target + /// Shape of earth + /// + /// The following example moves a coordinate 10km towards a target coordinate using + /// ellipsoidal earth calculations. + /// + /// //N 25º 0' 0" E 25º 0' 0" + /// Coordinate coord = Coordinate(25,25); + /// + /// //Target Coordinate + /// Coordinate target = new Coordinate(26.5, 23.2); + /// + /// Distance distance = new Distance(10, DistanceType.Kilometers); + /// + /// //Move coordinate the specified distance + /// //towards target using ellipsoidal calculations + /// coord.Move(target, distance, Shape.Ellipsoid); + /// + /// //New Coordinate - N 24º 56' 21.526" E 25º 4' 23.944" + /// + /// + public void Move(Coordinate target, Distance distance, Shape shape) + { + Distance d = new Distance(this, target, shape); + //Convert to Radians for formula + Double lat1 = this.latitude.ToRadians(); + Double lon1 = this.longitude.ToRadians(); + Double crs12 = d.Bearing * Math.PI / 180; //Convert bearing to radians + + Double[] ellipse = new Double[] { this.equatorial_radius, this.inverse_flattening }; + + if (shape == Shape.Sphere) { + Double[] cd = Distance_Assistant.Direct(lat1, lon1, crs12, distance.Meters); + Double lat2 = cd[0] * (180 / Math.PI); + Double lon2 = cd[1] * (180 / Math.PI); + + //ADJUST CORD + this.Latitude.DecimalDegree = lat2; + this.Longitude.DecimalDegree = -lon2; //v2.1.1.1 update + } else { + Double[] cde = Distance_Assistant.Direct_Ell(lat1, -lon1, crs12, distance.Meters, ellipse); + //Convert back from radians + Double lat2 = cde[0] * (180 / Math.PI); + Double lon2 = cde[1] * (180 / Math.PI); //v2.1.1.1 + //ADJUST CORD + this.Latitude.DecimalDegree = lat2; + this.Longitude.DecimalDegree = lon2; + } + } + + /// + /// Attempts to parse a string into a Coordinate. + /// + /// Coordinate string + /// Coordinate + /// boolean + /// + /// + /// Coordinate c; + /// if(Coordinate.TryParse("N 32.891º W 64.872º",out c)) + /// { + /// Console.WriteLine(c); //N 32º 53' 28.212" W 64º 52' 20.914" + /// } + /// + /// + public static Boolean TryParse(String s, out Coordinate c) + { + c = null; + if (FormatFinder.TryParse(s, CartesianType.Cartesian, out c)) { + Parse_Format_Type pft = c.Parse_Format; + c = new Coordinate(c.Latitude.ToDouble(), c.Longitude.ToDouble()) { + parse_Format = pft + }; //Reset with EagerLoad back on. + + return true; + } + return false; + } + /// + /// Attempts to parse a string into a Coordinate with specified DateTime + /// + /// Coordinate string + /// GeoDate + /// Coordinate + /// boolean + /// + /// + /// Coordinate c; + /// if(Coordinate.TryParse("N 32.891º W 64.872º", new DateTime(2018,7,7), out c)) + /// { + /// Console.WriteLine(c); //N 32º 53' 28.212" W 64º 52' 20.914" + /// } + /// + /// + public static Boolean TryParse(String s, DateTime geoDate, out Coordinate c) + { + c = null; + if (FormatFinder.TryParse(s, CartesianType.Cartesian, out c)) { + Parse_Format_Type pft = c.Parse_Format; + c = new Coordinate(c.Latitude.ToDouble(), c.Longitude.ToDouble(), geoDate) { + parse_Format = pft + }; //Reset with EagerLoad back on. + + return true; + } + return false; + } + /// + /// Attempts to parse a string into a Coordinate. + /// + /// Coordinate string + /// Coordinate + /// Cartesian Type + /// boolean + /// + /// + /// Coordinate c; + /// if(Coordinate.TryParse("N 32.891º W 64.872º", CartesianType.Cartesian, out c)) + /// { + /// Console.WriteLine(c); //N 32º 53' 28.212" W 64º 52' 20.914" + /// } + /// + /// + public static Boolean TryParse(String s, CartesianType ct, out Coordinate c) + { + c = null; + if (FormatFinder.TryParse(s, ct, out c)) { + Parse_Format_Type pft = c.Parse_Format; + if (ct == CartesianType.ECEF) { + Distance h = c.ecef.GeoDetic_Height; + c = new Coordinate(c.Latitude.ToDouble(), c.Longitude.ToDouble()); //Reset with EagerLoad back on. + c.ecef.Set_GeoDetic_Height(c, h); + } else { + c = new Coordinate(c.Latitude.ToDouble(), c.Longitude.ToDouble()); //Reset with EagerLoad back on. + } + c.parse_Format = pft; + + return true; + } + return false; + } + /// + /// Attempts to parse a string into a Coordinate with specified DateTime + /// + /// Coordinate string + /// GeoDate + /// Coordinate + /// Cartesian Type + /// boolean + /// + /// + /// Coordinate c; + /// if(Coordinate.TryParse("N 32.891º W 64.872º", new DateTime(2018,7,7), CartesianType.Cartesian, out c)) + /// { + /// Console.WriteLine(c); //N 32º 53' 28.212" W 64º 52' 20.914" + /// } + /// + /// + public static Boolean TryParse(String s, DateTime geoDate, CartesianType ct, out Coordinate c) + { + c = null; + if (FormatFinder.TryParse(s, ct, out c)) { + Parse_Format_Type pft = c.Parse_Format; + if (ct == CartesianType.ECEF) { + Distance h = c.ecef.GeoDetic_Height; + c = new Coordinate(c.Latitude.ToDouble(), c.Longitude.ToDouble(), geoDate); //Reset with EagerLoad back on. + c.ecef.Set_GeoDetic_Height(c, h); + } else { + c = new Coordinate(c.Latitude.ToDouble(), c.Longitude.ToDouble(), geoDate); //Reset with EagerLoad back on. + } + c.parse_Format = pft; + + return true; + } + return false; + } + + /// + /// Property changed event + /// + public event PropertyChangedEventHandler PropertyChanged; + /// + /// Notify property changed + /// + /// Property name + public void NotifyPropertyChanged(String propName) + { + switch (propName) { + case "CelestialInfo": + if (!this.EagerLoadSettings.Celestial || this.CelestialInfo == null) { return; } //Prevent Null Exceptions and calls while eagerloading is off + this.CelestialInfo.CalculateCelestialTime(this.latitude.DecimalDegree, this.longitude.DecimalDegree, this.geoDate); + break; + case "UTM": + if (!this.EagerLoadSettings.UTM_MGRS || this.UTM == null) { return; } + this.UTM.ToUTM(this.latitude.ToDouble(), this.longitude.ToDouble(), this.UTM); + break; + case "utm": + //Adjust case and notify of change. + //Use to notify without calling ToUTM() + propName = "UTM"; + break; + case "MGRS": + if (!this.EagerLoadSettings.UTM_MGRS || this.MGRS == null) { return; } + this.MGRS.ToMGRS(this.UTM); + break; + case "Cartesian": + if (!this.EagerLoadSettings.Cartesian || this.Cartesian == null) { return; } + this.Cartesian.ToCartesian(this); + break; + case "ECEF": + if (!this.EagerLoadSettings.ECEF) { return; } + this.ECEF.ToECEF(this); + break; + default: + break; + } + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName)); + } + } + /// + /// Observable class for handling latitudinal and longitudinal coordinate parts. + /// + /// + /// Objects can be passed to Coordinate object Latitude and Longitude properties. + /// + [Serializable] + public class CoordinatePart : INotifyPropertyChanged + { + //Defaults: + //Format: Degrees Minutes Seconds + //Rounding: Dependent upon selected format + //Leading Zeros: False + //Trailing Zeros: False + //Display Symbols: True (All Symbols display) + //Display Hyphens: False + //Position Display: First + + private Double decimalDegree; + private Double decimalMinute; + private Int32 degrees; + private Int32 minutes; + private Double seconds; + private CoordinatesPosition position; + private readonly CoordinateType type; + + internal Coordinate parent; + /// + /// Used to determine and notify the CoordinatePart parent Coordinate object. + /// + public Coordinate Parent => this.parent; + + /// + /// Observable decimal format coordinate. + /// + public Double DecimalDegree + { + get => this.decimalDegree; + set { + //If changing, notify the needed property changes + if (this.decimalDegree != value) { + //Validate the value + if (this.type == CoordinateType.Lat) { + if (value > 90) { + throw new ArgumentOutOfRangeException("Degrees out of range", "Latitude degrees cannot be greater than 90"); + } + if (value < -90) { + throw new ArgumentOutOfRangeException("Degrees out of range", "Latitude degrees cannot be less than -90"); } - leadString += "."; - if (isTrail) - { - for (int i = 0; i < rounding; i++) - { - leadString += "0"; - } + } + if (this.type == CoordinateType.Long) { + if (value > 180) { + throw new ArgumentOutOfRangeException("Degrees out of range", "Longitude degrees cannot be greater than 180"); } - else - { - leadString += "#########"; + if (value < -180) { + throw new ArgumentOutOfRangeException("Degrees out of range", "Longitude degrees cannot be less than -180"); } - leadString += "}"; - return leadString; + } + this.decimalDegree = value; - } + //Update Position + if ((this.position == CoordinatesPosition.N || this.position == CoordinatesPosition.E) && this.decimalDegree < 0) { + this.position = this.type == CoordinateType.Lat ? CoordinatesPosition.S : CoordinatesPosition.W; - private string FormatError(string argument, string rule) - { - return "'" + argument + "' is not a valid argument for string format rule: " + rule + "."; - } + } + if ((this.position == CoordinatesPosition.W || this.position == CoordinatesPosition.S) && this.decimalDegree >= 0) { + this.position = this.type == CoordinateType.Lat ? CoordinatesPosition.N : CoordinatesPosition.E; - private enum ToStringType - { - Decimal_Degree, Degree_Decimal_Minute, Degree_Minute_Second, Decimal - } - /// - /// Notify the correct properties and parent properties. - /// - /// Property Type - private void NotifyProperties(PropertyTypes p) - { - switch (p) - { - case PropertyTypes.DecimalDegree: - NotifyPropertyChanged("DecimalDegree"); - NotifyPropertyChanged("DecimalMinute"); - NotifyPropertyChanged("Degrees"); - NotifyPropertyChanged("Minutes"); - NotifyPropertyChanged("Seconds"); - NotifyPropertyChanged("Position"); - break; - case PropertyTypes.DecimalMinute: - NotifyPropertyChanged("DecimalDegree"); - NotifyPropertyChanged("DecimalMinute"); - NotifyPropertyChanged("Minutes"); - NotifyPropertyChanged("Seconds"); - break; - case PropertyTypes.Degree: - NotifyPropertyChanged("DecimalDegree"); - NotifyPropertyChanged("Degree"); - break; - case PropertyTypes.Minute: - NotifyPropertyChanged("DecimalDegree"); - NotifyPropertyChanged("DecimalMinute"); - NotifyPropertyChanged("Minutes"); - break; - case PropertyTypes.Position: - NotifyPropertyChanged("DecimalDegree"); - NotifyPropertyChanged("Position"); - break; - case PropertyTypes.Second: - NotifyPropertyChanged("DecimalDegree"); - NotifyPropertyChanged("DecimalMinute"); - NotifyPropertyChanged("Seconds"); - break; - default: - NotifyPropertyChanged("DecimalDegree"); - NotifyPropertyChanged("DecimalMinute"); - NotifyPropertyChanged("Degrees"); - NotifyPropertyChanged("Minutes"); - NotifyPropertyChanged("Seconds"); - NotifyPropertyChanged("Position"); - break; - } - NotifyPropertyChanged("Display"); + } + //Update the Degree & Decimal Minute + Double degABS = Math.Abs(this.decimalDegree); //Make decimalDegree positive for calculations + Double degFloor = Math.Truncate(degABS); //Truncate the number leftto extract the degree + Decimal f = Convert.ToDecimal(degFloor); //Convert to degree to decimal to keep precision during calculations + Decimal ddm = Convert.ToDecimal(degABS) - f; //Extract decimalMinute value from decimalDegree + ddm *= 60; //Multiply by 60 to get readable decimalMinute - if (Parent != null) - { - Parent.NotifyPropertyChanged("Display"); - Parent.NotifyPropertyChanged("CelestialInfo"); - Parent.NotifyPropertyChanged("UTM"); - Parent.NotifyPropertyChanged("MGRS"); - Parent.NotifyPropertyChanged("Cartesian"); - Parent.NotifyPropertyChanged("ECEF"); - } + Double dm = Convert.ToDouble(ddm); //Convert decimalMinutes back to double for storage + Int32 df = Convert.ToInt32(degFloor); //Convert degrees to int for storage - } + if (this.degrees != df) { + this.degrees = df; - /// - /// Property changed event. - /// - public event PropertyChangedEventHandler PropertyChanged; - /// - /// Notify property changed - /// - /// Property name - public void NotifyPropertyChanged(string propName) - { - if (PropertyChanged != null) - { - PropertyChanged(this, new PropertyChangedEventArgs(propName)); - } - } + } + if (this.decimalMinute != dm) { + this.decimalMinute = dm; - /// - /// Used for notifying the correct properties. - /// - private enum PropertyTypes - { - DecimalDegree, DecimalMinute, Position, Degree, Minute, Second, FormatChange - } + } + //Update Minutes Seconds + Double dmFloor = Math.Floor(dm); //Get number left of decimal to grab minute value + Int32 mF = Convert.ToInt32(dmFloor); //Convert minute to int for storage + f = Convert.ToDecimal(dmFloor); //Create a second minute value and store as decimal for precise calculation - /// - /// Returns CoordinatePart in radians - /// - /// - public double ToRadians() - { - return decimalDegree * Math.PI / 180; + Decimal s = ddm - f; //Get seconds from minutes + s *= 60; //Multiply by 60 to get readable seconds + Double secs = Convert.ToDouble(s); //Convert back to double for storage + + if (this.minutes != mF) { + this.minutes = mF; + + } + if (this.seconds != secs) { + this.seconds = secs; + } + this.NotifyProperties(PropertyTypes.DecimalDegree); } - /// - /// Attempts to parse a string into a CoordinatePart. - /// - /// CoordinatePart string - /// CoordinatePart - /// boolean - /// - /// - /// CoordinatePart cp; - /// if(CoordinatePart.TryParse("N 32.891º", out cp)) - /// { - /// Console.WriteLine(cp); //N 32º 53' 28.212" - /// } - /// - /// - public static bool TryParse(string s, out CoordinatePart cp) - { - cp = null; - - if (FormatFinder_CoordPart.TryParse(s, out cp)) - { - return true; - } - return false; - } - /// - /// Attempts to parse a string into a CoordinatePart. - /// - /// CoordinatePart string - /// CoordinateType - /// CoordinatePart - /// boolean - /// - /// - /// CoordinatePart cp; - /// if(CoordinatePart.TryParse("-32.891º", CoordinateType.Long, out cp)) - /// { - /// Console.WriteLine(cp); //W 32º 53' 27.6" - /// } - /// - /// - public static bool TryParse(string s, CoordinateType t, out CoordinatePart cp) - { - cp = null; - //Comma at beginning parses to long - //Asterik forces lat - if(t== CoordinateType.Long) { s = "," + s; } - else { s = "*" + s; } - if (FormatFinder_CoordPart.TryParse(s, out cp)) - { - return true; - } - return false; + } + } + /// + /// Observable decimal format minute. + /// + public Double DecimalMinute + { + get => this.decimalMinute; + set { + if (this.decimalMinute != value) { + if (value < 0) { value *= -1; }//Adjust accidental negative input + //Validate values + + Decimal dm = Math.Abs(Convert.ToDecimal(value)) / 60; + Double decMin = Convert.ToDouble(dm); + if (this.type == CoordinateType.Lat) { + + if (this.degrees + decMin > 90) { throw new ArgumentOutOfRangeException("Degrees out of range", "Latitudinal degrees cannot be greater than 90"); } + } else { + if (this.degrees + decMin > 180) { throw new ArgumentOutOfRangeException("Degrees out of range", "Longitudinal degrees cannot be greater than 180"); } + } + if (value >= 60) { throw new ArgumentOutOfRangeException("Minutes out of range", "Coordinate Minutes cannot be greater than or equal to 60"); } + if (value < 0) { throw new ArgumentOutOfRangeException("Minutes out of range", "Coordinate Minutes cannot be less than 0"); } + + + this.decimalMinute = value; + + + Decimal decValue = Convert.ToDecimal(value); //Convert value to decimal for precision during calculation + Decimal dmFloor = Math.Floor(decValue); //Extract minutes + Decimal secs = decValue - dmFloor; //Extract seconds + secs *= 60; //Convert seconds to human readable format + + Decimal newDM = decValue / 60; //divide decimalMinute by 60 to get storage value + Decimal newDD = this.degrees + newDM;//Add new decimal value to the floor degree value to get new decimalDegree; + if (this.decimalDegree < 0) { newDD = newDD * -1; } //Restore negative if needed + + this.decimalDegree = Convert.ToDouble(newDD); //Convert back to double for storage + + + this.minutes = Convert.ToInt32(dmFloor); //Convert minutes to int for storage + + this.seconds = Convert.ToDouble(secs); //Convert seconds to double for storage + this.NotifyProperties(PropertyTypes.DecimalMinute); } + } } -} + /// + /// Observable coordinate degree. + /// + public Int32 Degrees + { + get => this.degrees; + set { + //Validate Value + if (this.degrees != value) { + + if (value < 0) { value *= -1; }//Adjust accidental negative input + + if (this.type == CoordinateType.Lat) { + if (value + this.decimalMinute / 100.0 > 90) { + throw new ArgumentOutOfRangeException("Degrees", "Latitude degrees cannot be greater than 90"); + } + } + if (this.type == CoordinateType.Long) { + if (value + this.decimalMinute / 100.0 > 180) { + throw new ArgumentOutOfRangeException("Degrees", "Longitude degrees cannot be greater than 180"); + } + + } + + Decimal f = Convert.ToDecimal(this.degrees); + + this.degrees = value; + + Double degABS = Math.Abs(this.decimalDegree); //Make decimalDegree positive for calculations + Decimal dDec = Convert.ToDecimal(degABS); //Convert to Decimal for precision during calculations + //Convert degrees to decimal to keep precision + Decimal dm = dDec - f; //Extract minutes + Decimal newDD = this.degrees + dm; //Add minutes to new degree for decimalDegree + + if (this.decimalDegree < 0) { newDD *= -1; } //Set negative as required + + this.decimalDegree = Convert.ToDouble(newDD); // Convert decimalDegree to double for storage + this.NotifyProperties(PropertyTypes.Degree); + } + } + } + /// + /// Observable coordinate minute. + /// + public Int32 Minutes + { + get => this.minutes; + set { + if (this.minutes != value) { + if (value < 0) { value *= -1; }//Adjust accidental negative input + //Validate the minutes + Decimal vMin = Convert.ToDecimal(value); + if (this.type == CoordinateType.Lat) { + if (this.degrees + (vMin / 60) > 90) { throw new ArgumentOutOfRangeException("Degrees out of range", "Latitudinal degrees cannot be greater than 90"); } + } else { + if (this.degrees + (vMin / 60) > 180) { throw new ArgumentOutOfRangeException("Degrees out of range", "Longitudinal degrees cannot be greater than 180"); } + } + if (value >= 60) { + throw new ArgumentOutOfRangeException("Minutes out of range", "Minutes cannot be greater than or equal to 60"); + } + if (value < 0) { + throw new ArgumentOutOfRangeException("Minutes out of range", "Minutes cannot be less than 0"); + } + Decimal minFloor = Convert.ToDecimal(this.minutes);//Convert decimal to minutes for calculation + Decimal f = Convert.ToDecimal(this.degrees); //Convert to degree to keep precision during calculation + + this.minutes = value; + + + Double degABS = Math.Abs(this.decimalDegree); //Make decimalDegree positive + Decimal dDec = Convert.ToDecimal(degABS); //Convert to decimalDegree for precision during calucation + + Decimal dm = dDec - f; //Extract minutes + dm *= 60; //Make minutes human readable + + Decimal secs = dm - minFloor;//Extract Seconds + + Decimal newDM = this.minutes + secs;//Add seconds to minutes for decimalMinute + Double decMin = Convert.ToDouble(newDM); //Convert decimalMinute to double for storage + this.decimalMinute = decMin; //Round to correct precision + + + newDM /= 60; //Convert decimalMinute to storage format + Decimal newDeg = f + newDM; //Add value to degree for decimalDegree + if (this.decimalDegree < 0) { newDeg *= -1; }// Set to negative as required. + this.decimalDegree = Convert.ToDouble(newDeg);//Convert to double and roun to correct precision for storage + this.NotifyProperties(PropertyTypes.Minute); + } + } + } + /// + /// Observable coordinate second. + /// + public Double Seconds + { + get => this.seconds; + set { + if (value < 0) { value *= -1; }//Adjust accidental negative input + if (this.seconds != value) { + //Validate Seconds + Decimal vSec = Convert.ToDecimal(value); + vSec /= 60; + + Decimal vMin = Convert.ToDecimal(this.minutes); + vMin += vSec; + vMin /= 60; + + if (this.type == CoordinateType.Lat) { + if (this.degrees + vMin > 90) { throw new ArgumentOutOfRangeException("Degrees out of range", "Latitudinal degrees cannot be greater than 90"); } + } else { + if (this.degrees + vMin > 180) { throw new ArgumentOutOfRangeException("Degrees out of range", "Longitudinal degrees cannot be greater than 180"); } + } + if (value >= 60) { + throw new ArgumentOutOfRangeException("Seconds out of range", "Seconds cannot be greater than or equal to 60"); + } + if (value < 0) { + throw new ArgumentOutOfRangeException("Seconds out of range", "Seconds cannot be less than 0"); + } + this.seconds = value; + + + Double degABS = Math.Abs(this.decimalDegree); //Make decimalDegree positive + Double degFloor = Math.Truncate(degABS); //Truncate the number left of the decimal + Decimal f = Convert.ToDecimal(degFloor); //Convert to decimal to keep precision + + Decimal secs = Convert.ToDecimal(this.seconds); //Convert seconds to decimal for calculations + secs /= 60; //Convert to storage format + Decimal dm = this.minutes + secs;//Add seconds to minutes for decimalMinute + Double minFD = Convert.ToDouble(dm); //Convert decimalMinute for storage + this.decimalMinute = minFD;//Round to proper precision + + Decimal nm = Convert.ToDecimal(this.decimalMinute) / 60;//Convert decimalMinute to decimal and divide by 60 to get storage format decimalMinute + Double newDeg = this.degrees + Convert.ToDouble(nm);//Convert to double and add to degree for storage decimalDegree + if (this.decimalDegree < 0) { newDeg *= -1; }//Make negative as needed + this.decimalDegree = newDeg;//Update decimalDegree and round to proper precision + this.NotifyProperties(PropertyTypes.Second); + } + } + } + /// + /// Formate coordinate part string. + /// + public String Display => this.parent != null ? this.ToString(this.parent.FormatOptions) : this.ToString(); + /// + /// Observable coordinate position. + /// + public CoordinatesPosition Position + { + get => this.position; + set { + if (this.position != value) { + if (this.type == CoordinateType.Long && (value == CoordinatesPosition.N || value == CoordinatesPosition.S)) { + throw new InvalidOperationException("You cannot change a Longitudinal type coordinate into a Latitudinal"); + } + if (this.type == CoordinateType.Lat && (value == CoordinatesPosition.E || value == CoordinatesPosition.W)) { + throw new InvalidOperationException("You cannot change a Latitudinal type coordinate into a Longitudinal"); + } + this.decimalDegree *= -1; // Change the position + this.position = value; + this.NotifyProperties(PropertyTypes.Position); + } + } + } + + /// + /// Creates an empty CoordinatePart. + /// + /// CoordinateType + /// Parent Coordinate object + [Obsolete("Method is deprecated. You no longer need to pass a Coordinate object through the constructor.")] + public CoordinatePart(CoordinateType t, Coordinate c) + { + this.parent = c; + this.type = t; + this.decimalDegree = 0; + this.degrees = 0; + this.minutes = 0; + this.seconds = 0; + this.position = this.type == CoordinateType.Lat ? CoordinatesPosition.N : CoordinatesPosition.E; + } + /// + /// Creates a populated CoordinatePart from a decimal format part. + /// + /// Coordinate decimal value + /// Coordinate type + /// Parent Coordinate object + [Obsolete("Method is deprecated. You no longer need to pass a Coordinate object through the constructor.")] + public CoordinatePart(Double value, CoordinateType t, Coordinate c) + { + this.parent = c; + this.type = t; + + if (this.type == CoordinateType.Long) { + if (value > 180) { throw new ArgumentOutOfRangeException("Degrees out of range", "Longitudinal coordinate decimal cannot be greater than 180."); } + if (value < -180) { throw new ArgumentOutOfRangeException("Degrees out of range", "Longitudinal coordinate decimal cannot be less than 180."); } + this.position = value < 0 ? CoordinatesPosition.W : CoordinatesPosition.E; + } else { + if (value > 90) { throw new ArgumentOutOfRangeException("Degrees out of range", "Latitudinal coordinate decimal cannot be greater than 90."); } + if (value < -90) { throw new ArgumentOutOfRangeException("Degrees out of range", "Latitudinal coordinate decimal cannot be less than 90."); } + this.position = value < 0 ? CoordinatesPosition.S : CoordinatesPosition.N; + } + Decimal dd = Convert.ToDecimal(value); + dd = Math.Abs(dd); + Decimal ddFloor = Math.Floor(dd);//DEGREE + Decimal dm = dd - ddFloor; + dm *= 60; //DECIMAL MINUTE + Decimal dmFloor = Math.Floor(dm); //MINUTES + Decimal sec = dm - dmFloor; + sec *= 60;//SECONDS + + + this.decimalDegree = value; + this.degrees = Convert.ToInt32(ddFloor); + this.minutes = Convert.ToInt32(dmFloor); + this.decimalMinute = Convert.ToDouble(dm); + this.seconds = Convert.ToDouble(sec); + } + /// + /// Creates a populated CoordinatePart object from a Degrees Minutes Seconds part. + /// + /// Degrees + /// Minutes + /// Seconds + /// Coordinate Part Position + /// Parent Coordinate + [Obsolete("Method is deprecated. You no longer need to pass a Coordinate object through the constructor.")] + public CoordinatePart(Int32 deg, Int32 min, Double sec, CoordinatesPosition pos, Coordinate c) + { + this.parent = c; + this.type = pos == CoordinatesPosition.N || pos == CoordinatesPosition.S ? CoordinateType.Lat : CoordinateType.Long; + + if (deg < 0) { throw new ArgumentOutOfRangeException("Degrees out of range", "Degrees cannot be less than 0."); } + if (min < 0) { throw new ArgumentOutOfRangeException("Minutes out of range", "Minutes cannot be less than 0."); } + if (sec < 0) { throw new ArgumentOutOfRangeException("Seconds out of range", "Seconds cannot be less than 0."); } + if (min >= 60) { throw new ArgumentOutOfRangeException("Minutes out of range", "Minutes cannot be greater than or equal to 60."); } + if (sec >= 60) { throw new ArgumentOutOfRangeException("Seconds out of range", "Seconds cannot be greater than or equal to 60."); } + this.degrees = deg; + this.minutes = min; + this.seconds = sec; + this.position = pos; + + Decimal secD = Convert.ToDecimal(sec); + secD /= 60; //Decimal Seconds + Decimal minD = Convert.ToDecimal(min); + minD += secD; //Decimal Minutes + + if (this.type == CoordinateType.Long) { + if (deg + (minD / 60) > 180) { throw new ArgumentOutOfRangeException("Degrees out of range", "Longitudinal Degrees cannot be greater than 180."); } + } else { + if (deg + (minD / 60) > 90) { throw new ArgumentOutOfRangeException("Degrees out of range", "Latitudinal Degrees cannot be greater than 90."); } + } + this.decimalMinute = Convert.ToDouble(minD); + Decimal dd = Convert.ToDecimal(deg) + (minD / 60); + + + if (pos == CoordinatesPosition.S || pos == CoordinatesPosition.W) { + dd *= -1; + } + this.decimalDegree = Convert.ToDouble(dd); + } + /// + /// Creates a populated CoordinatePart from a Degrees Minutes Seconds part. + /// + /// Degrees + /// Decimal Minutes + /// Coordinate Part Position + /// Parent Coordinate object + [Obsolete("Method is deprecated. You no longer need to pass a Coordinate object through the constructor.")] + public CoordinatePart(Int32 deg, Double minSec, CoordinatesPosition pos, Coordinate c) + { + this.parent = c; + + this.type = pos == CoordinatesPosition.N || pos == CoordinatesPosition.S ? CoordinateType.Lat : CoordinateType.Long; + + if (deg < 0) { throw new ArgumentOutOfRangeException("Degree out of range", "Degree cannot be less than 0."); } + if (minSec < 0) { throw new ArgumentOutOfRangeException("Minutes out of range", "Minutes cannot be less than 0."); } + + if (minSec >= 60) { throw new ArgumentOutOfRangeException("Minutes out of range", "Minutes cannot be greater than or equal to 60."); } + + if (this.type == CoordinateType.Lat) { + if (deg + (minSec / 60) > 90) { throw new ArgumentOutOfRangeException("Degree out of range", "Latitudinal degrees cannot be greater than 90."); } + } else { + if (deg + (minSec / 60) > 180) { throw new ArgumentOutOfRangeException("Degree out of range", "Longitudinal degrees cannot be greater than 180."); } + } + this.degrees = deg; + this.decimalMinute = minSec; + this.position = pos; + + Decimal minD = Convert.ToDecimal(minSec); + Decimal minFloor = Math.Floor(minD); + this.minutes = Convert.ToInt32(minFloor); + Decimal sec = minD - minFloor; + sec *= 60; + Decimal secD = Convert.ToDecimal(sec); + this.seconds = Convert.ToDouble(secD); + Decimal dd = deg + (minD / 60); + + if (pos == CoordinatesPosition.S || pos == CoordinatesPosition.W) { + dd *= -1; + } + this.decimalDegree = Convert.ToDouble(dd); + } + + /// + /// Creates an empty CoordinatePart. + /// + /// CoordinateType + public CoordinatePart(CoordinateType t) + { + this.type = t; + this.decimalDegree = 0; + this.degrees = 0; + this.minutes = 0; + this.seconds = 0; + this.position = this.type == CoordinateType.Lat ? CoordinatesPosition.N : CoordinatesPosition.E; + } + /// + /// Creates a populated CoordinatePart from a decimal format part. + /// + /// Coordinate decimal value + /// Coordinate type + public CoordinatePart(Double value, CoordinateType t) + { + this.type = t; + + if (this.type == CoordinateType.Long) { + if (value > 180) { throw new ArgumentOutOfRangeException("Degrees out of range", "Longitudinal coordinate decimal cannot be greater than 180."); } + if (value < -180) { throw new ArgumentOutOfRangeException("Degrees out of range", "Longitudinal coordinate decimal cannot be less than 180."); } + this.position = value < 0 ? CoordinatesPosition.W : CoordinatesPosition.E; + } else { + if (value > 90) { throw new ArgumentOutOfRangeException("Degrees out of range", "Latitudinal coordinate decimal cannot be greater than 90."); } + if (value < -90) { throw new ArgumentOutOfRangeException("Degrees out of range", "Latitudinal coordinate decimal cannot be less than 90."); } + this.position = value < 0 ? CoordinatesPosition.S : CoordinatesPosition.N; + } + Decimal dd = Convert.ToDecimal(value); + dd = Math.Abs(dd); + Decimal ddFloor = Math.Floor(dd);//DEGREE + Decimal dm = dd - ddFloor; + dm *= 60; //DECIMAL MINUTE + Decimal dmFloor = Math.Floor(dm); //MINUTES + Decimal sec = dm - dmFloor; + sec *= 60;//SECONDS + + + this.decimalDegree = value; + this.degrees = Convert.ToInt32(ddFloor); + this.minutes = Convert.ToInt32(dmFloor); + this.decimalMinute = Convert.ToDouble(dm); + this.seconds = Convert.ToDouble(sec); + } + /// + /// Creates a populated CoordinatePart object from a Degrees Minutes Seconds part. + /// + /// Degrees + /// Minutes + /// Seconds + /// Coordinate Part Position + public CoordinatePart(Int32 deg, Int32 min, Double sec, CoordinatesPosition pos) + { + this.type = pos == CoordinatesPosition.N || pos == CoordinatesPosition.S ? CoordinateType.Lat : CoordinateType.Long; + + if (deg < 0) { throw new ArgumentOutOfRangeException("Degrees out of range", "Degrees cannot be less than 0."); } + if (min < 0) { throw new ArgumentOutOfRangeException("Minutes out of range", "Minutes cannot be less than 0."); } + if (sec < 0) { throw new ArgumentOutOfRangeException("Seconds out of range", "Seconds cannot be less than 0."); } + if (min >= 60) { throw new ArgumentOutOfRangeException("Minutes out of range", "Minutes cannot be greater than or equal to 60."); } + if (sec >= 60) { throw new ArgumentOutOfRangeException("Seconds out of range", "Seconds cannot be greater than or equal to 60."); } + this.degrees = deg; + this.minutes = min; + this.seconds = sec; + this.position = pos; + + Decimal secD = Convert.ToDecimal(sec); + secD /= 60; //Decimal Seconds + Decimal minD = Convert.ToDecimal(min); + minD += secD; //Decimal Minutes + + if (this.type == CoordinateType.Long) { + if (deg + (minD / 60) > 180) { throw new ArgumentOutOfRangeException("Degrees out of range", "Longitudinal Degrees cannot be greater than 180."); } + } else { + if (deg + (minD / 60) > 90) { throw new ArgumentOutOfRangeException("Degrees out of range", "Latitudinal Degrees cannot be greater than 90."); } + } + this.decimalMinute = Convert.ToDouble(minD); + Decimal dd = Convert.ToDecimal(deg) + (minD / 60); + + + if (pos == CoordinatesPosition.S || pos == CoordinatesPosition.W) { + dd *= -1; + } + this.decimalDegree = Convert.ToDouble(dd); + } + /// + /// Creates a populated CoordinatePart from a Degrees Minutes Seconds part. + /// + /// Degrees + /// Decimal Minutes + /// Coordinate Part Position + public CoordinatePart(Int32 deg, Double minSec, CoordinatesPosition pos) + { + this.type = pos == CoordinatesPosition.N || pos == CoordinatesPosition.S ? CoordinateType.Lat : CoordinateType.Long; + + if (deg < 0) { throw new ArgumentOutOfRangeException("Degree out of range", "Degree cannot be less than 0."); } + if (minSec < 0) { throw new ArgumentOutOfRangeException("Minutes out of range", "Minutes cannot be less than 0."); } + + if (minSec >= 60) { throw new ArgumentOutOfRangeException("Minutes out of range", "Minutes cannot be greater than or equal to 60."); } + + if (this.type == CoordinateType.Lat) { + if (deg + (minSec / 60) > 90) { throw new ArgumentOutOfRangeException("Degree out of range", "Latitudinal degrees cannot be greater than 90."); } + } else { + if (deg + (minSec / 60) > 180) { throw new ArgumentOutOfRangeException("Degree out of range", "Longitudinal degrees cannot be greater than 180."); } + } + this.degrees = deg; + this.decimalMinute = minSec; + this.position = pos; + + Decimal minD = Convert.ToDecimal(minSec); + Decimal minFloor = Math.Floor(minD); + this.minutes = Convert.ToInt32(minFloor); + Decimal sec = minD - minFloor; + sec *= 60; + Decimal secD = Convert.ToDecimal(sec); + this.seconds = Convert.ToDouble(secD); + Decimal dd = deg + (minD / 60); + + if (pos == CoordinatesPosition.S || pos == CoordinatesPosition.W) { + dd *= -1; + } + this.decimalDegree = Convert.ToDouble(dd); + } + + /// + /// Signed degrees (decimal) format coordinate. + /// + /// double + public Double ToDouble() => this.decimalDegree; + + /// + /// Overridden Coordinate ToString() method + /// + /// Dstring + public override String ToString() => this.parent == null ? this.FormatString(new CoordinateFormatOptions()) : this.FormatString(this.Parent.FormatOptions); + + /// + /// Formatted CoordinatePart string. + /// + /// CoordinateFormatOptions + /// string (formatted) + public String ToString(CoordinateFormatOptions options) => this.FormatString(options); + /// + /// String formatting logic + /// + /// CoordinateFormatOptions + /// Formatted coordinate part string + private String FormatString(CoordinateFormatOptions options) + { + ToStringType type = ToStringType.Degree_Minute_Second; + Int32? rounding = null; + Boolean lead = false; + Boolean trail = false; + Boolean hyphen = false; + Boolean symbols = true; + Boolean degreeSymbol = true; + Boolean minuteSymbol = true; + Boolean secondsSymbol = true; + Boolean positionFirst = true; + + #region Assign Formatting Rules + switch (options.Format) { + case CoordinateFormatType.Degree_Minutes_Seconds: + type = ToStringType.Degree_Minute_Second; + break; + case CoordinateFormatType.Degree_Decimal_Minutes: + type = ToStringType.Degree_Decimal_Minute; + break; + case CoordinateFormatType.Decimal_Degree: + type = ToStringType.Decimal_Degree; + break; + case CoordinateFormatType.Decimal: + type = ToStringType.Decimal; + break; + default: + type = ToStringType.Degree_Minute_Second; + break; + } + rounding = options.Round; + lead = options.Display_Leading_Zeros; + trail = options.Display_Trailing_Zeros; + symbols = options.Display_Symbols; + degreeSymbol = options.Display_Degree_Symbol; + minuteSymbol = options.Display_Minute_Symbol; + secondsSymbol = options.Display_Seconds_Symbol; + hyphen = options.Display_Hyphens; + positionFirst = options.Position_First; + #endregion + + switch (type) { + case ToStringType.Decimal_Degree: + if (rounding == null) { rounding = 6; } + return this.ToDecimalDegreeString(rounding.Value, lead, trail, symbols, degreeSymbol, positionFirst, hyphen); + case ToStringType.Degree_Decimal_Minute: + if (rounding == null) { rounding = 3; } + return this.ToDegreeDecimalMinuteString(rounding.Value, lead, trail, symbols, degreeSymbol, minuteSymbol, hyphen, positionFirst); + case ToStringType.Degree_Minute_Second: + if (rounding == null) { rounding = 3; } + return this.ToDegreeMinuteSecondString(rounding.Value, lead, trail, symbols, degreeSymbol, minuteSymbol, secondsSymbol, hyphen, positionFirst); + case ToStringType.Decimal: + if (rounding == null) { rounding = 9; } + Double dub = this.ToDouble(); + dub = Math.Round(dub, rounding.Value); + String lt = this.Leading_Trailing_Format(lead, trail, rounding.Value, this.Position); + return String.Format(lt, dub); + } + + return String.Empty; + } + //DMS Coordinate Format + private String ToDegreeMinuteSecondString(Int32 rounding, Boolean lead, Boolean trail, Boolean symbols, Boolean degreeSymbol, Boolean minuteSymbol, Boolean secondSymbol, Boolean hyphen, Boolean positionFirst) + { + + String leadString = this.Leading_Trailing_Format(lead, false, rounding, this.Position); + String d = String.Format(leadString, this.Degrees); // Degree String + String minute = lead ? String.Format("{0:00}", this.Minutes) : this.Minutes.ToString(); + String leadTrail = this.Leading_Trailing_Format(lead, trail, rounding); + + Double sc = Math.Round(this.Seconds, rounding); + String second = String.Format(leadTrail, sc); + String hs = " "; + String ds = ""; + String ms = ""; + String ss = ""; + if (symbols) { + if (degreeSymbol) { ds = "º"; } + if (minuteSymbol) { ms = "'"; } + if (secondSymbol) { ss = "\""; } + } + if (hyphen) { hs = "-"; } + + return positionFirst + ? this.Position.ToString() + hs + d + ds + hs + minute + ms + hs + second + ss + : d + ds + hs + minute + ms + hs + second + ss + hs + this.Position.ToString(); + } + //DDM Coordinate Format + private String ToDegreeDecimalMinuteString(Int32 rounding, Boolean lead, Boolean trail, Boolean symbols, Boolean degreeSymbol, Boolean minuteSymbol, Boolean hyphen, Boolean positionFirst) + { + String leadString = "{0:0"; + if (lead) { + if (this.Position == CoordinatesPosition.E || this.Position == CoordinatesPosition.W) { + leadString += "00"; + } else { + leadString += "0"; + } + } + leadString += "}"; + String d = String.Format(leadString, this.Degrees); // Degree String + + String leadTrail = "{0:0"; + if (lead) { + leadTrail += "0"; + } + leadTrail += "."; + if (trail) { + for (Int32 i = 0; i < rounding; i++) { + leadTrail += "0"; + } + } else { + leadTrail += "#########"; + } + leadTrail += "}"; + + Double ns = this.Seconds / 60; + Double c = Math.Round(this.Minutes + ns, rounding); + if (c == 60 && this.Degrees + 1 < 91) { c = 0; d = String.Format(leadString, this.Degrees + 1); }//Adjust for rounded maxed out Seconds. will Convert 42 60.0 to 43 + String ms = String.Format(leadTrail, c); + String hs = " "; + String ds = ""; + String ss = ""; + if (symbols) { + if (degreeSymbol) { ds = "º"; } + if (minuteSymbol) { ss = "'"; } + } + if (hyphen) { hs = "-"; } + + return positionFirst ? this.Position.ToString() + hs + d + ds + hs + ms + ss : d + ds + hs + ms + ss + hs + this.Position.ToString(); + + } + ////DD Coordinate Format + private String ToDecimalDegreeString(Int32 rounding, Boolean lead, Boolean trail, Boolean symbols, Boolean degreeSymbol, Boolean positionFirst, Boolean hyphen) + { + String degreeS = ""; + String hyph = " "; + if (degreeSymbol) { degreeS = "º"; } + if (!symbols) { degreeS = ""; } + if (hyphen) { hyph = "-"; } + + String leadTrail = "{0:0"; + if (lead) { + if (this.Position == CoordinatesPosition.E || this.Position == CoordinatesPosition.W) { + leadTrail += "00"; + } else { + leadTrail += "0"; + } + } + leadTrail += "."; + if (trail) { + for (Int32 i = 0; i < rounding; i++) { + leadTrail += "0"; + } + } else { + leadTrail += "#########"; + } + leadTrail += "}"; + + Double result = (this.Degrees) + (Convert.ToDouble(this.Minutes)) / 60 + (Convert.ToDouble(this.Seconds)) / 3600; + result = Math.Round(result, rounding); + String d = String.Format(leadTrail, Math.Abs(result)); + return positionFirst ? this.Position.ToString() + hyph + d + degreeS : d + degreeS + hyph + this.Position.ToString(); + + } + + private String Leading_Trailing_Format(Boolean isLead, Boolean isTrail, Int32 rounding, CoordinatesPosition? p = null) + { + String leadString = "{0:0"; + if (isLead) { + if (p != null) { + if (p.Value == CoordinatesPosition.W || p.Value == CoordinatesPosition.E) { + leadString += "00"; + } + } else { + leadString += "0"; + } + } + + leadString += "."; + if (isTrail) { + for (Int32 i = 0; i < rounding; i++) { + leadString += "0"; + } + } else { + leadString += "#########"; + } + + leadString += "}"; + return leadString; + + } + + private String FormatError(String argument, String rule) => "'" + argument + "' is not a valid argument for string format rule: " + rule + "."; + + private enum ToStringType + { + Decimal_Degree, Degree_Decimal_Minute, Degree_Minute_Second, Decimal + } + /// + /// Notify the correct properties and parent properties. + /// + /// Property Type + private void NotifyProperties(PropertyTypes p) + { + switch (p) { + case PropertyTypes.DecimalDegree: + this.NotifyPropertyChanged("DecimalDegree"); + this.NotifyPropertyChanged("DecimalMinute"); + this.NotifyPropertyChanged("Degrees"); + this.NotifyPropertyChanged("Minutes"); + this.NotifyPropertyChanged("Seconds"); + this.NotifyPropertyChanged("Position"); + break; + case PropertyTypes.DecimalMinute: + this.NotifyPropertyChanged("DecimalDegree"); + this.NotifyPropertyChanged("DecimalMinute"); + this.NotifyPropertyChanged("Minutes"); + this.NotifyPropertyChanged("Seconds"); + break; + case PropertyTypes.Degree: + this.NotifyPropertyChanged("DecimalDegree"); + this.NotifyPropertyChanged("Degree"); + break; + case PropertyTypes.Minute: + this.NotifyPropertyChanged("DecimalDegree"); + this.NotifyPropertyChanged("DecimalMinute"); + this.NotifyPropertyChanged("Minutes"); + break; + case PropertyTypes.Position: + this.NotifyPropertyChanged("DecimalDegree"); + this.NotifyPropertyChanged("Position"); + break; + case PropertyTypes.Second: + this.NotifyPropertyChanged("DecimalDegree"); + this.NotifyPropertyChanged("DecimalMinute"); + this.NotifyPropertyChanged("Seconds"); + break; + default: + this.NotifyPropertyChanged("DecimalDegree"); + this.NotifyPropertyChanged("DecimalMinute"); + this.NotifyPropertyChanged("Degrees"); + this.NotifyPropertyChanged("Minutes"); + this.NotifyPropertyChanged("Seconds"); + this.NotifyPropertyChanged("Position"); + break; + } + this.NotifyPropertyChanged("Display"); + + if (this.Parent != null) { + this.Parent.NotifyPropertyChanged("Display"); + this.Parent.NotifyPropertyChanged("CelestialInfo"); + this.Parent.NotifyPropertyChanged("UTM"); + this.Parent.NotifyPropertyChanged("MGRS"); + this.Parent.NotifyPropertyChanged("Cartesian"); + this.Parent.NotifyPropertyChanged("ECEF"); + } + + } + + /// + /// Property changed event. + /// + public event PropertyChangedEventHandler PropertyChanged; + /// + /// Notify property changed + /// + /// Property name + public void NotifyPropertyChanged(String propName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName)); + + /// + /// Used for notifying the correct properties. + /// + private enum PropertyTypes + { + DecimalDegree, DecimalMinute, Position, Degree, Minute, Second, FormatChange + } + + /// + /// Returns CoordinatePart in radians + /// + /// + public Double ToRadians() => this.decimalDegree * Math.PI / 180; + /// + /// Attempts to parse a string into a CoordinatePart. + /// + /// CoordinatePart string + /// CoordinatePart + /// boolean + /// + /// + /// CoordinatePart cp; + /// if(CoordinatePart.TryParse("N 32.891º", out cp)) + /// { + /// Console.WriteLine(cp); //N 32º 53' 28.212" + /// } + /// + /// + public static Boolean TryParse(String s, out CoordinatePart cp) + { + cp = null; + + return FormatFinder_CoordPart.TryParse(s, out cp) ? true : false; + } + /// + /// Attempts to parse a string into a CoordinatePart. + /// + /// CoordinatePart string + /// CoordinateType + /// CoordinatePart + /// boolean + /// + /// + /// CoordinatePart cp; + /// if(CoordinatePart.TryParse("-32.891º", CoordinateType.Long, out cp)) + /// { + /// Console.WriteLine(cp); //W 32º 53' 27.6" + /// } + /// + /// + public static Boolean TryParse(String s, CoordinateType t, out CoordinatePart cp) + { + cp = null; + //Comma at beginning parses to long + //Asterik forces lat + s = t == CoordinateType.Long ? "," + s : "*" + s; + return FormatFinder_CoordPart.TryParse(s, out cp) ? true : false; + } + + } +} \ No newline at end of file diff --git a/CoordinateSharp/CoordinateSharp.csproj b/CoordinateSharp/CoordinateSharp.csproj index 0d71e98..d265715 100644 --- a/CoordinateSharp/CoordinateSharp.csproj +++ b/CoordinateSharp/CoordinateSharp.csproj @@ -5,13 +5,14 @@ Debug AnyCPU {DA8510CE-7899-49DD-9E17-7C974382288D} - Exe + Library CoordinateSharp CoordinateSharp - v4.7.2 + v4.7.1 512 true true + AnyCPU @@ -32,6 +33,9 @@ prompt 4 + + +