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
+
+
+