diff --git a/GeoMagSharp/Calculator.cs b/GeoMagSharp/Calculator.cs index 8b3aeb9..516de98 100644 --- a/GeoMagSharp/Calculator.cs +++ b/GeoMagSharp/Calculator.cs @@ -1,20 +1,24 @@ /**************************************************************************** - * File: GeoMagBGGM.cs - * Description: routines to handle bggm coefficients file and calculate - * field values - * Akowlegements: Ported from the C++ model code created by the British Geological Survey - * Website: http://www.geomag.bgs.ac.uk/data_service/directionaldrilling/bggm.html - * Warnings: This code can be used with the IGRF, WMM, or BGGM coeficent file. The BGGM - * coeficient file is Commerically avalable from the British Geological Survey - * and is not distributed with this project. Please contcact the BGS for more information. - * + * File: Calculator.cs + * Description: Routines to calculate magnetic field values from + * spherical harmonic coefficient files + * Reference: Based on the NOAA World Magnetic Model (WMM) algorithm + * https://www.ncei.noaa.gov/products/world-magnetic-model + * Compatibility: This code can be used with IGRF, WMM, or other standard + * coefficient files in COF or DAT format. * Current version: 2.21 - * ****************************************************************************/ + * Author: Christopher Strecker + * Website: https://github.com/StreckerCM/GeoMagSharpGUI + ****************************************************************************/ using System; namespace GeoMagSharp { + /// + /// Static calculator for magnetic field values using spherical harmonic coefficients. + /// Based on the NOAA World Magnetic Model (WMM) algorithm. + /// public static class Calculator { diff --git a/GeoMagSharp/Enums/GeoMagEnums.cs b/GeoMagSharp/Enums/GeoMagEnums.cs index abb9ede..a57a8ba 100644 --- a/GeoMagSharp/Enums/GeoMagEnums.cs +++ b/GeoMagSharp/Enums/GeoMagEnums.cs @@ -29,7 +29,7 @@ public enum CoordinateSystem public enum Algorithm { /// - /// British Geological Survey algorithm + /// Default spherical harmonic algorithm /// BGS = 1, diff --git a/GeoMagSharp/ExtensionMethods.cs b/GeoMagSharp/ExtensionMethods.cs index 4566deb..92bf64c 100644 --- a/GeoMagSharp/ExtensionMethods.cs +++ b/GeoMagSharp/ExtensionMethods.cs @@ -11,6 +11,9 @@ namespace GeoMagSharp { + /// + /// Extension methods for date/time conversion, angle conversion, and model identification. + /// public static class ExtensionMethods { /// @@ -167,11 +170,21 @@ public static DateTime ToDateTime(this double decDate) return new DateTime(yearInt, monthInt, dayInt); } + /// + /// Determines whether the year of the specified date falls within the valid range (1900 to DateTime.MaxValue.Year). + /// + /// The date to validate. + /// true if the year is within the valid range; otherwise, false. public static bool IsValidYear(this DateTime date) { return (1900 <= date.Year && date.Year <= DateTime.MaxValue.Year); } + /// + /// Determines whether the specified decimal year falls within the valid range (1900 to max DateTime as decimal). + /// + /// The decimal year to validate. + /// true if the decimal year is within the valid range; otherwise, false. public static bool IsValidYear(this double decDate) { return (1900D <= decDate && decDate <= DateTime.MaxValue.ToDecimal()); diff --git a/GeoMagSharp/GeoMag.cs b/GeoMagSharp/GeoMag.cs index f6131e2..aaef4e0 100644 --- a/GeoMagSharp/GeoMag.cs +++ b/GeoMagSharp/GeoMag.cs @@ -1,231 +1,269 @@ -/**************************************************************************** - * File: GeoMag.cs - * Description: Routines to provide an interface to the calculation methods - * Author: Christopher Strecker - * Website: https://github.com/StreckerCM/GeoMagSharpGUI - * Warnings: - * Current version: - * ****************************************************************************/ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.IO; - - -namespace GeoMagSharp -{ - public class GeoMag - { - public List ResultsOfCalculation; - - private MagneticModelSet _Models; - - private CalculationOptions _CalculationOptions; - - public GeoMag() - { - _Models = null; - } - - public void LoadModel(string modelFile) - { - _Models = null; - - if (string.IsNullOrEmpty(modelFile)) - throw new GeoMagExceptionFileNotFound("Error coefficient file name not specified"); - - _Models = ModelReader.Read(modelFile); - - } - - public void LoadModel(MagneticModelSet modelSet) - { - _Models = null; - - if (modelSet == null) - throw new GeoMagExceptionFileNotFound("Error coefficient file name not specified"); - - _Models = modelSet; - - } - - public void LoadModel(string modelFile, string svFile) - { - _Models = null; - - if (string.IsNullOrEmpty(modelFile)) - throw new GeoMagExceptionFileNotFound("Error coefficient file name not specified"); - - _Models = ModelReader.Read(modelFile, svFile); - - } - - public void MagneticCalculations(CalculationOptions inCalculationOptions) - { - _CalculationOptions = null; - ResultsOfCalculation = null; - - if (_Models == null || _Models.NumberOfModels.Equals(0)) - throw new GeoMagExceptionModelNotLoaded("Error: No models avaliable for calculation"); - - if (!_Models.IsDateInRange(inCalculationOptions.StartDate)) - { - throw new GeoMagExceptionOutOfRange(string.Format("Error: the date {0} is out of range for this model{1}The valid date range for the is {2} to {3}", - inCalculationOptions.StartDate.ToShortDateString(), Environment.NewLine, _Models.MinDate.ToDateTime().ToShortDateString(), - _Models.MaxDate.ToDateTime().ToShortDateString())); - - } - - if (inCalculationOptions.EndDate.Equals(DateTime.MinValue)) inCalculationOptions.EndDate = inCalculationOptions.StartDate; - - if (!_Models.IsDateInRange(inCalculationOptions.EndDate)) - { - throw new GeoMagExceptionOutOfRange(string.Format("Error: the date {0} is out of range for this model{1}The valid date range for the is {2} to {3}", - inCalculationOptions.EndDate.ToShortDateString(), Environment.NewLine, _Models.MinDate.ToDateTime().ToShortDateString(), - _Models.MaxDate.ToDateTime().ToShortDateString())); - } - - TimeSpan timespan = (inCalculationOptions.EndDate.Date - inCalculationOptions.StartDate.Date); - - double dayInc = inCalculationOptions.StepInterval < 1 ? 1 : inCalculationOptions.StepInterval; - - double dateIdx = 0; - - ResultsOfCalculation = new List(); - - _CalculationOptions = new CalculationOptions(inCalculationOptions); - - while (dateIdx <= timespan.Days) - { - - var internalSH = new Coefficients(); - - var externalSH = new Coefficients(); - - DateTime intervalDate = _CalculationOptions.StartDate.AddDays(dateIdx); - - _Models.GetIntExt(intervalDate.ToDecimal(), out internalSH, out externalSH); - - var magCalcDate = Calculator.SpotCalculation(_CalculationOptions, intervalDate, _Models, internalSH, externalSH, _Models.EarthRadius); - - if (magCalcDate != null) ResultsOfCalculation.Add(magCalcDate); - - dateIdx = ((dateIdx < timespan.Days) && ((dateIdx + dayInc) > timespan.Days)) - ? timespan.Days - : dateIdx + dayInc; - - } - - } - - - public void SaveResults(string fileName, bool loadAfterSave = false) - { - if (ResultsOfCalculation == null) - throw new GeoMagExceptionModelNotLoaded("Error: No calculation results to save"); - - if (ModelReader.IsFileLocked(fileName)) - throw new GeoMagExceptionOpenError(string.Format("Error: The file '{0}' is locked by another user or application", - Path.GetFileName(fileName))); - - if (File.Exists(fileName)) - { - - try - { - File.Delete(fileName); - } - catch (Exception e) - { - - throw new GeoMagExceptionOpenError(string.Format("Error: The file '{0}' could not be deleted: {1}", - System.IO.Path.GetFileName(fileName), e.ToString())); - } - - } - - Int32 lineCount = 0; - - //Int32 lineNumColIdx = -1; - - var tabStrRight = new StringBuilder(); - - //Build header - - - - tabStrRight.AppendFormat("{0}:\t{1}{2}", "Model".PadLeft(15, ' '), Path.GetFileNameWithoutExtension(_Models.Name).ToUpper(), Environment.NewLine); - lineCount++; - - tabStrRight.AppendFormat("{0}:\t{1}{2}", "latitude".PadLeft(15, ' '), _CalculationOptions.Latitude.ToString("F7"), Environment.NewLine); - lineCount++; - - tabStrRight.AppendFormat("{0}:\t{1}{2}", "longitude".PadLeft(15, ' '), _CalculationOptions.Longitude.ToString("F7"), Environment.NewLine); - lineCount++; - - var elevation = _CalculationOptions.GetElevation; - - tabStrRight.AppendFormat("{0}:\t{1}\t{2}{3}", string.Format("{0}", elevation[0]).PadLeft(15, ' '), Convert.ToDouble(elevation[1]).ToString("F4"), elevation[2], Environment.NewLine); - lineCount++; - - tabStrRight.AppendFormat("{0}", Environment.NewLine); - lineCount++; - - const Int32 padlen = 25; - - const string rowFormat = "{0}\t{1}\t{2}\t{3}\t{4}\t{5}\t{6}\t{7}{8}"; - - //Build Column Header - - tabStrRight.AppendFormat(rowFormat, - "Date".PadRight(padlen, ' '), "Declination (+E/W)".PadRight(padlen, ' '), "Inclination (+D/-U)".PadRight(padlen, ' '), - "Horizontal Intensity".PadRight(padlen, ' '), "North Comp (+N/-S)".PadRight(padlen, ' '), "East Comp (+E/-W)".PadRight(padlen, ' '), - "Vertical Comp (+D/-U)".PadRight(padlen, ' '), "Total Field".PadRight(padlen, ' '), Environment.NewLine); - lineCount++; - - tabStrRight.AppendFormat(rowFormat, - "".PadRight(padlen, ' '), "deg".PadRight(padlen, ' '), "deg".PadRight(padlen, ' '), - "nT".PadRight(padlen, ' '), "nT".PadRight(padlen, ' '), "nT".PadRight(padlen, ' '), - "nT".PadRight(padlen, ' '), "nT".PadRight(padlen, ' '), Environment.NewLine); - lineCount++; - - tabStrRight.AppendFormat("{0}", Environment.NewLine); - lineCount++; - - //Build result rows - - foreach(var result in ResultsOfCalculation) - { - //Date - tabStrRight.AppendFormat(rowFormat, - result.Date.ToString("MM/dd/yyyy").PadRight(padlen, ' '), result.Declination.Value.ToString("F3").PadRight(padlen, ' '), - result.Inclination.Value.ToString("F3").PadRight(padlen, ' '), result.HorizontalIntensity.Value.ToString("F2").PadRight(padlen, ' '), - result.NorthComp.Value.ToString("F2").PadRight(padlen, ' '), result.EastComp.Value.ToString("F2").PadRight(padlen, ' '), - result.VerticalComp.Value.ToString("F2").PadRight(padlen, ' '), result.TotalField.Value.ToString("F2").PadRight(padlen, ' '), - Environment.NewLine); - - lineCount++; - } - - tabStrRight.AppendFormat(rowFormat, - "Change Per year".PadRight(padlen, ' '), ResultsOfCalculation.First().Declination.ChangePerYear.ToString("F3").PadRight(padlen, ' '), - ResultsOfCalculation.First().Inclination.ChangePerYear.ToString("F3").PadRight(padlen, ' '), ResultsOfCalculation.First().HorizontalIntensity.ChangePerYear.ToString("F2").PadRight(padlen, ' '), - ResultsOfCalculation.First().NorthComp.ChangePerYear.ToString("F2").PadRight(padlen, ' '), ResultsOfCalculation.First().EastComp.ChangePerYear.ToString("F2").PadRight(padlen, ' '), - ResultsOfCalculation.First().VerticalComp.ChangePerYear.ToString("F2").PadRight(padlen, ' '), ResultsOfCalculation.First().TotalField.ChangePerYear.ToString("F2").PadRight(padlen, ' '), - Environment.NewLine); - - // Write the stream contents to a text fle - using (StreamWriter outFile = File.AppendText(fileName)) - { - outFile.Write(tabStrRight.ToString()); - } - - - - - } - - } -} +/**************************************************************************** + * File: GeoMag.cs + * Description: Routines to provide an interface to the calculation methods + * Author: Christopher Strecker + * Website: https://github.com/StreckerCM/GeoMagSharpGUI + ****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; + + +namespace GeoMagSharp +{ + /// + /// Provides an interface to magnetic field calculation methods. + /// Handles model loading, magnetic field computation, and result export. + /// + public class GeoMag + { + /// + /// The results of the most recent magnetic field calculation. + /// + public List ResultsOfCalculation; + + private MagneticModelSet _Models; + + private CalculationOptions _CalculationOptions; + + /// + /// Initializes a new instance of the class. + /// + public GeoMag() + { + _Models = null; + } + + /// + /// Loads a magnetic model from a coefficient file. + /// + /// Path to the coefficient file (.COF or .DAT format) + /// Thrown when is null or empty. + public void LoadModel(string modelFile) + { + _Models = null; + + if (string.IsNullOrEmpty(modelFile)) + throw new GeoMagExceptionFileNotFound("Error coefficient file name not specified"); + + _Models = ModelReader.Read(modelFile); + + } + + /// + /// Loads a magnetic model from a pre-built . + /// + /// The magnetic model set to use for calculations. + /// Thrown when is null. + public void LoadModel(MagneticModelSet modelSet) + { + _Models = null; + + if (modelSet == null) + throw new GeoMagExceptionFileNotFound("Error coefficient file name not specified"); + + _Models = modelSet; + + } + + /// + /// Loads a magnetic model from separate main field and secular variation coefficient files. + /// + /// Path to the main field coefficient file. + /// Path to the secular variation coefficient file. + /// Thrown when is null or empty. + public void LoadModel(string modelFile, string svFile) + { + _Models = null; + + if (string.IsNullOrEmpty(modelFile)) + throw new GeoMagExceptionFileNotFound("Error coefficient file name not specified"); + + _Models = ModelReader.Read(modelFile, svFile); + + } + + /// + /// Performs magnetic field calculations over the specified date range and location. + /// Results are stored in . + /// + /// The calculation parameters including location, dates, and elevation. + /// Thrown when no model has been loaded. + /// Thrown when the start or end date is outside the model's valid range. + public void MagneticCalculations(CalculationOptions inCalculationOptions) + { + _CalculationOptions = null; + ResultsOfCalculation = null; + + if (_Models == null || _Models.NumberOfModels.Equals(0)) + throw new GeoMagExceptionModelNotLoaded("Error: No models avaliable for calculation"); + + if (!_Models.IsDateInRange(inCalculationOptions.StartDate)) + { + throw new GeoMagExceptionOutOfRange(string.Format("Error: the date {0} is out of range for this model{1}The valid date range for the is {2} to {3}", + inCalculationOptions.StartDate.ToShortDateString(), Environment.NewLine, _Models.MinDate.ToDateTime().ToShortDateString(), + _Models.MaxDate.ToDateTime().ToShortDateString())); + + } + + if (inCalculationOptions.EndDate.Equals(DateTime.MinValue)) inCalculationOptions.EndDate = inCalculationOptions.StartDate; + + if (!_Models.IsDateInRange(inCalculationOptions.EndDate)) + { + throw new GeoMagExceptionOutOfRange(string.Format("Error: the date {0} is out of range for this model{1}The valid date range for the is {2} to {3}", + inCalculationOptions.EndDate.ToShortDateString(), Environment.NewLine, _Models.MinDate.ToDateTime().ToShortDateString(), + _Models.MaxDate.ToDateTime().ToShortDateString())); + } + + TimeSpan timespan = (inCalculationOptions.EndDate.Date - inCalculationOptions.StartDate.Date); + + double dayInc = inCalculationOptions.StepInterval < 1 ? 1 : inCalculationOptions.StepInterval; + + double dateIdx = 0; + + ResultsOfCalculation = new List(); + + _CalculationOptions = new CalculationOptions(inCalculationOptions); + + while (dateIdx <= timespan.Days) + { + + var internalSH = new Coefficients(); + + var externalSH = new Coefficients(); + + DateTime intervalDate = _CalculationOptions.StartDate.AddDays(dateIdx); + + _Models.GetIntExt(intervalDate.ToDecimal(), out internalSH, out externalSH); + + var magCalcDate = Calculator.SpotCalculation(_CalculationOptions, intervalDate, _Models, internalSH, externalSH, _Models.EarthRadius); + + if (magCalcDate != null) ResultsOfCalculation.Add(magCalcDate); + + dateIdx = ((dateIdx < timespan.Days) && ((dateIdx + dayInc) > timespan.Days)) + ? timespan.Days + : dateIdx + dayInc; + + } + + } + + + /// + /// Saves the calculation results to a tab-separated text file. + /// + /// The output file path. + /// Reserved for future use. + /// Thrown when no calculation results are available. + /// Thrown when the file is locked or cannot be deleted. + public void SaveResults(string fileName, bool loadAfterSave = false) + { + if (ResultsOfCalculation == null) + throw new GeoMagExceptionModelNotLoaded("Error: No calculation results to save"); + + if (ModelReader.IsFileLocked(fileName)) + throw new GeoMagExceptionOpenError(string.Format("Error: The file '{0}' is locked by another user or application", + Path.GetFileName(fileName))); + + if (File.Exists(fileName)) + { + + try + { + File.Delete(fileName); + } + catch (Exception e) + { + + throw new GeoMagExceptionOpenError(string.Format("Error: The file '{0}' could not be deleted: {1}", + System.IO.Path.GetFileName(fileName), e.ToString())); + } + + } + + Int32 lineCount = 0; + + //Int32 lineNumColIdx = -1; + + var tabStrRight = new StringBuilder(); + + //Build header + + + + tabStrRight.AppendFormat("{0}:\t{1}{2}", "Model".PadLeft(15, ' '), Path.GetFileNameWithoutExtension(_Models.Name).ToUpper(), Environment.NewLine); + lineCount++; + + tabStrRight.AppendFormat("{0}:\t{1}{2}", "latitude".PadLeft(15, ' '), _CalculationOptions.Latitude.ToString("F7"), Environment.NewLine); + lineCount++; + + tabStrRight.AppendFormat("{0}:\t{1}{2}", "longitude".PadLeft(15, ' '), _CalculationOptions.Longitude.ToString("F7"), Environment.NewLine); + lineCount++; + + var elevation = _CalculationOptions.GetElevation; + + tabStrRight.AppendFormat("{0}:\t{1}\t{2}{3}", string.Format("{0}", elevation[0]).PadLeft(15, ' '), Convert.ToDouble(elevation[1]).ToString("F4"), elevation[2], Environment.NewLine); + lineCount++; + + tabStrRight.AppendFormat("{0}", Environment.NewLine); + lineCount++; + + const Int32 padlen = 25; + + const string rowFormat = "{0}\t{1}\t{2}\t{3}\t{4}\t{5}\t{6}\t{7}{8}"; + + //Build Column Header + + tabStrRight.AppendFormat(rowFormat, + "Date".PadRight(padlen, ' '), "Declination (+E/W)".PadRight(padlen, ' '), "Inclination (+D/-U)".PadRight(padlen, ' '), + "Horizontal Intensity".PadRight(padlen, ' '), "North Comp (+N/-S)".PadRight(padlen, ' '), "East Comp (+E/-W)".PadRight(padlen, ' '), + "Vertical Comp (+D/-U)".PadRight(padlen, ' '), "Total Field".PadRight(padlen, ' '), Environment.NewLine); + lineCount++; + + tabStrRight.AppendFormat(rowFormat, + "".PadRight(padlen, ' '), "deg".PadRight(padlen, ' '), "deg".PadRight(padlen, ' '), + "nT".PadRight(padlen, ' '), "nT".PadRight(padlen, ' '), "nT".PadRight(padlen, ' '), + "nT".PadRight(padlen, ' '), "nT".PadRight(padlen, ' '), Environment.NewLine); + lineCount++; + + tabStrRight.AppendFormat("{0}", Environment.NewLine); + lineCount++; + + //Build result rows + + foreach(var result in ResultsOfCalculation) + { + //Date + tabStrRight.AppendFormat(rowFormat, + result.Date.ToString("MM/dd/yyyy").PadRight(padlen, ' '), result.Declination.Value.ToString("F3").PadRight(padlen, ' '), + result.Inclination.Value.ToString("F3").PadRight(padlen, ' '), result.HorizontalIntensity.Value.ToString("F2").PadRight(padlen, ' '), + result.NorthComp.Value.ToString("F2").PadRight(padlen, ' '), result.EastComp.Value.ToString("F2").PadRight(padlen, ' '), + result.VerticalComp.Value.ToString("F2").PadRight(padlen, ' '), result.TotalField.Value.ToString("F2").PadRight(padlen, ' '), + Environment.NewLine); + + lineCount++; + } + + tabStrRight.AppendFormat(rowFormat, + "Change Per year".PadRight(padlen, ' '), ResultsOfCalculation.First().Declination.ChangePerYear.ToString("F3").PadRight(padlen, ' '), + ResultsOfCalculation.First().Inclination.ChangePerYear.ToString("F3").PadRight(padlen, ' '), ResultsOfCalculation.First().HorizontalIntensity.ChangePerYear.ToString("F2").PadRight(padlen, ' '), + ResultsOfCalculation.First().NorthComp.ChangePerYear.ToString("F2").PadRight(padlen, ' '), ResultsOfCalculation.First().EastComp.ChangePerYear.ToString("F2").PadRight(padlen, ' '), + ResultsOfCalculation.First().VerticalComp.ChangePerYear.ToString("F2").PadRight(padlen, ' '), ResultsOfCalculation.First().TotalField.ChangePerYear.ToString("F2").PadRight(padlen, ' '), + Environment.NewLine); + + // Write the stream contents to a text fle + using (StreamWriter outFile = File.AppendText(fileName)) + { + outFile.Write(tabStrRight.ToString()); + } + + + + + } + + } +} diff --git a/GeoMagSharp/GeoMagException.cs b/GeoMagSharp/GeoMagException.cs index d9d12dd..2d43f4d 100644 --- a/GeoMagSharp/GeoMagException.cs +++ b/GeoMagSharp/GeoMagException.cs @@ -48,9 +48,9 @@ protected GeoMagException( } } - /// - /// Thrown when file is not found - /// + /// + /// Thrown when a magnetic model has not been loaded before performing calculations. + /// [Serializable] public class GeoMagExceptionModelNotLoaded : GeoMagException { @@ -75,9 +75,9 @@ public GeoMagExceptionModelNotLoaded(SerializationInfo info, StreamingContext co } - /// - /// Thrown when file is not found - /// + /// + /// Thrown when a coefficient file cannot be found at the specified path. + /// [Serializable] public class GeoMagExceptionFileNotFound : GeoMagException { @@ -101,9 +101,9 @@ public GeoMagExceptionFileNotFound(SerializationInfo info, StreamingContext cont } } - /// - /// Thrown when file has an invalid charater in a line - /// + /// + /// Thrown when a coefficient file contains an invalid character in a data line. + /// [Serializable] public class GeoMagExceptionBadCharacter : GeoMagException { @@ -128,9 +128,9 @@ public GeoMagExceptionBadCharacter(SerializationInfo info, StreamingContext cont } - /// - /// Thrown when file has an invalid number of coefficients - /// + /// + /// Thrown when a coefficient file contains an unexpected number of coefficients. + /// [Serializable] public class GeoMagExceptionBadNumberOfCoefficients : GeoMagException { @@ -154,9 +154,9 @@ public GeoMagExceptionBadNumberOfCoefficients(SerializationInfo info, StreamingC } } - /// - /// Thrown when file has an invalid number of coefficients - /// + /// + /// Thrown when a coefficient file cannot be opened or read. + /// [Serializable] public class GeoMagExceptionOpenError : GeoMagException { @@ -180,9 +180,9 @@ public GeoMagExceptionOpenError(SerializationInfo info, StreamingContext context } } - /// - /// Thrown when file has an invalid number of coefficients - /// + /// + /// Thrown when a calculation date or parameter is outside the valid range. + /// [Serializable] public class GeoMagExceptionOutOfRange : GeoMagException { @@ -206,9 +206,9 @@ public GeoMagExceptionOutOfRange(SerializationInfo info, StreamingContext contex } } - /// - /// Thrown when file has an invalid number of coefficients - /// + /// + /// Thrown when a calculation or model loading operation exceeds available memory. + /// [Serializable] public class GeoMagExceptionOutOfMemory : GeoMagException { diff --git a/GeoMagSharp/ModelReader.cs b/GeoMagSharp/ModelReader.cs index cadcd85..4db164c 100644 --- a/GeoMagSharp/ModelReader.cs +++ b/GeoMagSharp/ModelReader.cs @@ -1,12 +1,10 @@ /**************************************************************************** * File: ModelReader.cs - * Description: Routines read a given model file into the model structure - * to be used for calculation - * Author: Christopher Strecker - * Website: https://github.com/StreckerCM/GeoMagSharpGUI - * Warnings: - * Current version: - * ****************************************************************************/ + * Description: Routines to read magnetic model coefficient files into + * the model structure for calculation + * Author: Christopher Strecker + * Website: https://github.com/StreckerCM/GeoMagSharpGUI + ****************************************************************************/ using System; using System.Globalization; @@ -15,6 +13,9 @@ namespace GeoMagSharp { + /// + /// Reads and parses magnetic model coefficient files (COF/DAT) into model structures. + /// public static class ModelReader { /// diff --git a/GeoMagSharp/Models/Configuration/CalculationOptions.cs b/GeoMagSharp/Models/Configuration/CalculationOptions.cs index f28622c..30c254d 100644 --- a/GeoMagSharp/Models/Configuration/CalculationOptions.cs +++ b/GeoMagSharp/Models/Configuration/CalculationOptions.cs @@ -17,6 +17,9 @@ public class CalculationOptions { #region Constructors + /// + /// Initializes a new instance with default values (origin point, current date, default algorithm). + /// public CalculationOptions() { Latitude = 0; @@ -32,6 +35,10 @@ public CalculationOptions() ElevationIsAltitude = true; } + /// + /// Initializes a new instance by copying values from another . + /// + /// The source options to copy from. public CalculationOptions(CalculationOptions other) { Latitude = other.Latitude; @@ -49,16 +56,35 @@ public CalculationOptions(CalculationOptions other) #endregion + /// Geographic latitude in decimal degrees (-90 to +90). public double Latitude { get; set; } + + /// Geographic longitude in decimal degrees (-180 to +180). public double Longitude { get; set; } + + /// Start date for the calculation range. public DateTime StartDate { get; set; } + + /// End date for the calculation range. If equal to , defaults to . public DateTime EndDate { get; set; } + + /// Step interval in days between calculations in a date range. public double StepInterval { get; set; } + + /// Whether to calculate secular variation (annual rate of change). public bool SecularVariation { get; set; } + + /// The calculation algorithm to use. public Algorithm CalculationMethod { get; set; } #region Getters & Setters + /// + /// Sets the elevation value, unit, and type (altitude or depth). + /// + /// The elevation value. + /// The unit of measurement. + /// true for altitude above sea level; false for depth below sea level. public void SetElevation(double value, Distance.Unit unit, bool isAltitude = true) { ElevationValue = value; @@ -66,6 +92,9 @@ public void SetElevation(double value, Distance.Unit unit, bool isAltitude = tru ElevationIsAltitude = isAltitude; } + /// + /// Gets the elevation converted to depth in meters. Positive for depth, negative for altitude. + /// public double DepthInM { get @@ -93,6 +122,9 @@ public double DepthInM } } + /// + /// Gets the elevation converted to altitude in kilometers. Positive for altitude, negative for depth. + /// public double AltitudeInKm { get @@ -120,6 +152,9 @@ public double AltitudeInKm } } + /// + /// Gets the elevation as a list of [label, value, unit abbreviation] for display purposes. + /// public List GetElevation { get @@ -133,6 +168,9 @@ public List GetElevation } } + /// + /// Gets the geographic co-latitude (90 - latitude) in degrees. + /// public double CoLatitude { get diff --git a/GeoMagSharp/Models/Configuration/Preferences.cs b/GeoMagSharp/Models/Configuration/Preferences.cs index af9b0b7..1db5a06 100644 --- a/GeoMagSharp/Models/Configuration/Preferences.cs +++ b/GeoMagSharp/Models/Configuration/Preferences.cs @@ -25,36 +25,42 @@ public class Preferences #region Getters & Setters + /// Whether to display coordinates in decimal degrees (true) or degrees/minutes/seconds (false). public bool UseDecimalDegrees { get { return _UseDecimalDegrees; } set { _UseDecimalDegrees = value; } } + /// Whether to use altitude (true) or depth (false) for elevation input. public bool UseAltitude { get { return _UseAltitude; } set { _UseAltitude = value; } } + /// The unit for magnetic field intensity display (e.g., "nT"). public string FieldUnit { get { return _FieldUnit; } set { _FieldUnit = value; } } + /// The unit for altitude/depth display (e.g., "ft", "m"). public string AltitudeUnits { get { return _AltitudeUnits; } set { _AltitudeUnits = value; } } + /// Default latitude hemisphere ("N" or "S"). public string LatitudeHemisphere { get { return _LatitudeHemisphere; } set { _LatitudeHemisphere = value; } } + /// Default longitude hemisphere ("E" or "W"). public string LongitudeHemisphere { get { return _LongitudeHemisphere; } @@ -65,10 +71,17 @@ public string LongitudeHemisphere #region Constructors + /// + /// Initializes a new instance with default preferences. + /// public Preferences() { } + /// + /// Initializes a new instance by copying values from another . + /// + /// The source preferences to copy from. public Preferences(Preferences other) { UseDecimalDegrees = other.UseDecimalDegrees; @@ -80,6 +93,11 @@ public Preferences(Preferences other) #region Object Serializers + /// + /// Serializes the preferences to a JSON file. + /// + /// The file path to save to. + /// true if the save was successful; otherwise, false. public bool Save(string filename) { if (string.IsNullOrEmpty(filename)) return false; @@ -113,6 +131,11 @@ public bool Save(string filename) return wasSucessful; } + /// + /// Deserializes preferences from a JSON file. + /// + /// The file path to load from. + /// The loaded , or a new default instance if the file is missing or invalid. public static Preferences Load(string filename) { if (string.IsNullOrEmpty(filename)) return new Preferences(); diff --git a/GeoMagSharp/Models/Coordinates/Latitude.cs b/GeoMagSharp/Models/Coordinates/Latitude.cs index 7aa6117..beb816b 100644 --- a/GeoMagSharp/Models/Coordinates/Latitude.cs +++ b/GeoMagSharp/Models/Coordinates/Latitude.cs @@ -19,21 +19,39 @@ public class Latitude : Coordinate #region Constructors + /// + /// Initializes a new instance at the equator (0 degrees). + /// public Latitude() { Decimal = 0; } + /// + /// Initializes a new instance with the specified decimal degree value. + /// + /// Latitude in decimal degrees (-90 to +90). public Latitude(double inDecimal) { Decimal = inDecimal; } + /// + /// Initializes a new instance by copying another . + /// + /// The source latitude to copy. public Latitude(Latitude other) { Decimal = other.Decimal; } + /// + /// Initializes a new instance from degrees, minutes, seconds, and hemisphere direction. + /// + /// Degrees component. + /// Minutes component. + /// Seconds component. + /// Hemisphere direction ("N" or "S"). public Latitude(double inDegrees, double inMinutes, double inSeconds, string inDirection) { bool isPositive = inDirection.Equals("N", StringComparison.OrdinalIgnoreCase); diff --git a/GeoMagSharp/Models/Coordinates/Longitude.cs b/GeoMagSharp/Models/Coordinates/Longitude.cs index 0feb0b9..2e28de3 100644 --- a/GeoMagSharp/Models/Coordinates/Longitude.cs +++ b/GeoMagSharp/Models/Coordinates/Longitude.cs @@ -19,21 +19,39 @@ public class Longitude : Coordinate #region Constructors + /// + /// Initializes a new instance at the prime meridian (0 degrees). + /// public Longitude() { Decimal = 0; } + /// + /// Initializes a new instance with the specified decimal degree value. + /// + /// Longitude in decimal degrees (-180 to +180). public Longitude(double inDecimal) { Decimal = inDecimal; } - public Longitude(Latitude other) + /// + /// Initializes a new instance by copying another . + /// + /// The source longitude to copy. + public Longitude(Longitude other) { Decimal = other.Decimal; } + /// + /// Initializes a new instance from degrees, minutes, seconds, and hemisphere direction. + /// + /// Degrees component. + /// Minutes component. + /// Seconds component. + /// Hemisphere direction ("E" or "W"). public Longitude(double inDegrees, double inMinutes, double inSeconds, string inDirection) { bool isPositive = inDirection.Equals("E", StringComparison.OrdinalIgnoreCase); diff --git a/GeoMagSharp/Models/Magnetic/Coefficients.cs b/GeoMagSharp/Models/Magnetic/Coefficients.cs index c8223f2..545f3b6 100644 --- a/GeoMagSharp/Models/Magnetic/Coefficients.cs +++ b/GeoMagSharp/Models/Magnetic/Coefficients.cs @@ -18,12 +18,19 @@ public class Coefficients { #region Constructors + /// + /// Initializes a new instance with an empty coefficient list and zero max degree. + /// public Coefficients() { coeffs = new List(); MaxDegree = 0; } + /// + /// Initializes a new instance by copying coefficients from another . + /// + /// The source coefficients to copy. public Coefficients(Coefficients other) { coeffs = new List(); @@ -34,7 +41,10 @@ public Coefficients(Coefficients other) #endregion + /// The list of spherical harmonic coefficient values. public List coeffs { get; set; } + + /// The maximum spherical harmonic degree represented by these coefficients. public Int32 MaxDegree { get; set; } } } diff --git a/GeoMagSharp/Models/Magnetic/MagneticModel.cs b/GeoMagSharp/Models/Magnetic/MagneticModel.cs index 03381b0..1792e2b 100644 --- a/GeoMagSharp/Models/Magnetic/MagneticModel.cs +++ b/GeoMagSharp/Models/Magnetic/MagneticModel.cs @@ -18,6 +18,9 @@ public class MagneticModel { #region Constructors + /// + /// Initializes a new instance with default values. + /// public MagneticModel() { Type = string.Empty; @@ -26,13 +29,17 @@ public MagneticModel() SharmCoeff = new List(); } + /// + /// Initializes a new instance by copying values from another . + /// + /// The source model to copy. public MagneticModel(MagneticModel other) { Type = other.Type; Year = other.Year; SharmCoeff = new List(); - if (other.SharmCoeff.Any()) SharmCoeff.AddRange(SharmCoeff); + if (other.SharmCoeff.Any()) SharmCoeff.AddRange(other.SharmCoeff); } #endregion diff --git a/GeoMagSharp/Models/Magnetic/MagneticModelCollection.cs b/GeoMagSharp/Models/Magnetic/MagneticModelCollection.cs index b47bb5b..d6b5741 100644 --- a/GeoMagSharp/Models/Magnetic/MagneticModelCollection.cs +++ b/GeoMagSharp/Models/Magnetic/MagneticModelCollection.cs @@ -19,11 +19,15 @@ namespace GeoMagSharp /// public class MagneticModelCollection : IEnumerable { + /// The internal list of magnetic model sets. [JsonProperty(TypeNameHandling = TypeNameHandling.None)] public List TList { get; private set; } #region Constructors + /// + /// Initializes a new instance with an empty model list. + /// public MagneticModelCollection() { TList = new List(); @@ -33,21 +37,39 @@ public MagneticModelCollection() #region Base Class Methods + /// + /// Adds a model set to the collection. + /// + /// The model set to add. public void Add(MagneticModelSet item) { TList.Add(item); } + /// + /// Adds a range of model sets to the collection. + /// + /// The model sets to add. public void AddRange(IEnumerable collection) { TList.AddRange(collection); } + /// + /// Searches for a model set that matches the specified predicate. + /// + /// The predicate to match against. + /// The first matching model set, or null if not found. public MagneticModelSet Find(Predicate match) { return TList.Find(match); } + /// + /// Searches for all model sets that match the specified predicate. + /// + /// The predicate to match against. + /// A list of all matching model sets. public List FindAll(Predicate match) { return TList.FindAll(match); @@ -97,6 +119,10 @@ public bool AddOrReplace(MagneticModelSet item) return false; } + /// + /// Returns an enumerator that iterates through the model sets. + /// + /// An enumerator for the collection. public IEnumerator GetEnumerator() { return TList.GetEnumerator(); @@ -111,6 +137,11 @@ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() #region Object Serializers + /// + /// Serializes the collection to a JSON file. + /// + /// The file path to save to. + /// true if the save was successful; otherwise, false. public bool Save(string filename) { if (string.IsNullOrEmpty(filename)) return false; @@ -144,6 +175,11 @@ public bool Save(string filename) return wasSucessful; } + /// + /// Deserializes a collection from a JSON file. + /// + /// The file path to load from. + /// The loaded collection, or a new empty collection if the file is missing or invalid. public static MagneticModelCollection Load(string filename) { if (string.IsNullOrEmpty(filename)) return new MagneticModelCollection(); @@ -182,6 +218,10 @@ public static MagneticModelCollection Load(string filename) #region getters & setters + /// + /// Gets a DataTable representation of all models for UI data binding. + /// Columns: ID, ModelName, FileNames, DateMin, DateMax, NumberOfModels, Type. + /// public DataTable GetDataTable { get diff --git a/GeoMagSharp/Models/Magnetic/MagneticModelSet.cs b/GeoMagSharp/Models/Magnetic/MagneticModelSet.cs index 8f0131f..ffac2e4 100644 --- a/GeoMagSharp/Models/Magnetic/MagneticModelSet.cs +++ b/GeoMagSharp/Models/Magnetic/MagneticModelSet.cs @@ -20,11 +20,18 @@ public class MagneticModelSet { #region Constructors + /// + /// Initializes a new instance with an empty model list. + /// public MagneticModelSet() { Models = new List(); } + /// + /// Initializes a new instance by copying values from another . + /// + /// The source model set to copy. public MagneticModelSet(MagneticModelSet other) { ID = other.ID; @@ -44,6 +51,10 @@ public MagneticModelSet(MagneticModelSet other) #region Public Methods + /// + /// Adds a magnetic model and updates the date range to include the model's epoch. + /// + /// The magnetic model to add. public void AddModel(MagneticModel newModel) { if (newModel == null) return; @@ -57,6 +68,11 @@ public void AddModel(MagneticModel newModel) MaxDate = newModel.Year; } + /// + /// Adds a spherical harmonic coefficient to the model at the specified index. + /// + /// The index of the model to add the coefficient to. + /// The coefficient value. public void AddCoefficients(Int32 modelIdx, double coeff) { if (modelIdx.Equals(-1)) return; @@ -290,6 +306,7 @@ subtract what is needed at start and end of time span #region getters & setters + /// Unique identifier for this model set. public Guid ID { get @@ -304,6 +321,7 @@ public Guid ID } } + /// Display name of the model (e.g., "WMM2025"). public string Name { get @@ -318,6 +336,7 @@ public string Name } } + /// List of coefficient file paths used to build this model set. public List FileNames { get @@ -332,6 +351,7 @@ public List FileNames } } + /// The known model type (WMM, IGRF, EMM, etc.). public knownModels Type { get @@ -346,6 +366,7 @@ public knownModels Type } } + /// Minimum valid date (decimal year) for this model set. Only accepts values earlier than the current minimum. public double MinDate { get @@ -362,6 +383,7 @@ public double MinDate } } + /// Maximum valid date (decimal year) for this model set. Only accepts values later than the current maximum. public double MaxDate { get @@ -378,6 +400,7 @@ public double MaxDate } } + /// Earth's radius in kilometers used by this model. public double EarthRadius { get @@ -392,6 +415,7 @@ public double EarthRadius } } + /// Gets a copy of the internal models list. [JsonIgnore] public List GetModels { @@ -401,6 +425,7 @@ public List GetModels } } + /// Gets the number of models in the set, or -1 if models are null. [JsonIgnore] public Int32 NumberOfModels { diff --git a/GeoMagSharp/Models/Results/GeoMagVector.cs b/GeoMagSharp/Models/Results/GeoMagVector.cs index ff46387..61a0edb 100644 --- a/GeoMagSharp/Models/Results/GeoMagVector.cs +++ b/GeoMagSharp/Models/Results/GeoMagVector.cs @@ -14,6 +14,9 @@ public class GeoMagVector { #region Constructors + /// + /// Initializes a new instance with all components set to zero. + /// public GeoMagVector() { d = 0; @@ -25,6 +28,10 @@ public GeoMagVector() f = 0; } + /// + /// Initializes a new instance by copying all components from another . + /// + /// The source vector to copy. public GeoMagVector(GeoMagVector other) { d = other.d; diff --git a/GeoMagSharp/Models/Results/MagneticCalculations.cs b/GeoMagSharp/Models/Results/MagneticCalculations.cs index 592e8a7..aa5eac5 100644 --- a/GeoMagSharp/Models/Results/MagneticCalculations.cs +++ b/GeoMagSharp/Models/Results/MagneticCalculations.cs @@ -16,6 +16,9 @@ public class MagneticCalculations { #region Constructors + /// + /// Initializes a new instance with the current date and zero-valued components. + /// public MagneticCalculations() { Date = DateTime.Now; @@ -28,6 +31,10 @@ public MagneticCalculations() TotalField = new MagneticValue(); } + /// + /// Initializes a new instance by copying values from another . + /// + /// The source calculation results to copy. public MagneticCalculations(MagneticCalculations other) { Date = other.Date; @@ -40,6 +47,12 @@ public MagneticCalculations(MagneticCalculations other) TotalField = new MagneticValue(other.TotalField); } + /// + /// Initializes a new instance from field and secular variation vectors. + /// + /// The date of the calculation. + /// The main magnetic field vector. + /// The secular variation vector, or null if not computed. public MagneticCalculations(DateTime inDate, GeoMagVector fieldCalculations, GeoMagVector SecVarCalculations = null) { Date = inDate; diff --git a/GeoMagSharp/Models/Results/MagneticValue.cs b/GeoMagSharp/Models/Results/MagneticValue.cs index 76934b2..c5267d1 100644 --- a/GeoMagSharp/Models/Results/MagneticValue.cs +++ b/GeoMagSharp/Models/Results/MagneticValue.cs @@ -14,12 +14,19 @@ public class MagneticValue { #region Constructors + /// + /// Initializes a new instance with zero value and zero change per year. + /// public MagneticValue() { Value = 0.0; ChangePerYear = 0.0; } + /// + /// Initializes a new instance by copying values from another . + /// + /// The source value to copy. public MagneticValue(MagneticValue other) { Value = other.Value; diff --git a/GeoMagSharp/Units.cs b/GeoMagSharp/Units.cs index 99e97b7..23ba0d9 100644 --- a/GeoMagSharp/Units.cs +++ b/GeoMagSharp/Units.cs @@ -13,17 +13,33 @@ namespace GeoMagSharp { + /// + /// Provides distance and angle unit types with conversion between string and enum representations. + /// public static class Distance { + /// + /// Distance measurement units. + /// public enum Unit { + /// Unknown or unspecified unit. unknown = 0, + /// Meters. meter = 1, + /// Kilometers. kilometer = 2, + /// Feet. foot = 3, + /// Miles. mile = 4 } + /// + /// Converts a enum value to its abbreviation string. + /// + /// The distance unit to convert. + /// The unit abbreviation (e.g., "m", "ft", "mi"), or empty string for unknown. public static string ToString(Unit inUnit) { switch (inUnit) @@ -32,7 +48,7 @@ public static string ToString(Unit inUnit) return @"m"; case Distance.Unit.kilometer: - return @"mi"; + return @"km"; case Distance.Unit.foot: return @"ft"; @@ -45,6 +61,11 @@ public static string ToString(Unit inUnit) return string.Empty; } + /// + /// Parses a string to the corresponding enum value. + /// + /// The unit string (e.g., "m", "km", "ft", "meter"). + /// The matching , or if not recognized. public static Unit FromString(string unitString) { switch (unitString.ToLower()) @@ -73,15 +94,29 @@ public static Unit FromString(string unitString) return Distance.Unit.unknown; } + /// + /// Provides angle unit types with conversion between string and enum representations. + /// public static class Angle { + /// + /// Angle measurement units. + /// public enum Unit { + /// Unknown or unspecified unit. unknown = 0, + /// Degrees. Degree = 1, + /// Radians. Radian = 2 } + /// + /// Converts an enum value to its symbol string. + /// + /// The angle unit to convert. + /// The unit symbol (e.g., "°", "g"), or empty string for unknown. public static string ToString(Unit inUnit) { switch (inUnit) @@ -97,6 +132,11 @@ public static string ToString(Unit inUnit) return string.Empty; } + /// + /// Parses a string to the corresponding enum value. + /// + /// The unit string (e.g., "degree", "deg", "°", "radian"). + /// The matching , or if not recognized. public static Unit FromString(string unitString) { switch (unitString.ToLower()) diff --git a/README.md b/README.md index fa29d5a..02c1f2a 100644 --- a/README.md +++ b/README.md @@ -138,6 +138,5 @@ See [GeoMagGUI/documentation/LICENSE](./GeoMagGUI/documentation/LICENSE) ## Credits -- Original NOAA Geomag 7.0 software -- British Geological Survey (BGS) calculation algorithms +- NOAA World Magnetic Model (WMM) and Geomag 7.0 software - Port to C# with GUI by StreckerCM