VERSION 1.0 CLASS
BEGIN
  MultiUse = -1  'True
  Persistable = 0  'NotPersistable
  DataBindingBehavior = 0  'vbNone
  DataSourceBehavior  = 0  'vbNone
  MTSTransactionMode  = 0  'NotAnMTSObject
END
Attribute VB_Name = "clsNadCon"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = True

' Class Module **********************************************
' Name:        clsNadCon
' Purpose:     Calls Fortran library routines to convert
'              (1) horizontal datum: NAD1983 -> NAD1927, and
'                                    NAD1927 -> NAD1983, and
'              (2) altitude datum:  NGVD1929 -> NGVD1988, and
'                                   NGVD1988 -> NGVD1929.
'
' Authors:     Augenstein and Briel
' Date:        October 25, 1999
'
Option Explicit

Private m_objErrors As ErrorHandler.clsErrors

Private Declare Sub F90_NADINIT Lib "nadcon.dll" ( _
                      NadInitErrorFlag As Long, _
                      ByVal FullPathToAreapar As String, _
                      ByVal LengthOfFullPath As Integer)

Private Declare Sub F90_NADCON Lib "nadcon.dll" ( _
                      ConversionDirectionFlag As Long, _
                      InputLongDecDeg As Double, _
                      InputLatiDecDeg As Double, _
                      OutputLongDecDeg As Double, _
                      OutputLatiDecDeg As Double, _
                      NadConErrorFlag As Long)

Private Declare Sub F90_HMS Lib "nadcon.dll" ( _
                      InputDecDeg As Double, _
                      OutputDegrees As Long, _
                      OutputMinutes As Long, _
                      OutputSeconds As Double)

Private Declare Sub F90_VERTINIT Lib "nadcon.dll" ( _
                      VertInitErrorFlag As Long)

Private Declare Sub F90_VERTCON Lib "nadcon.dll" ( _
                      InputLongDecDeg As Double, _
                      InputLatiDecDeg As Double, _
                      OutputAltitudeCorrection As Single, _
                      VertConErrorFlag As Long)

Private Declare Sub F90_W99OPN Lib "Hass_ent.dll" ()

Private Declare Sub F90_W99CLO Lib "Hass_ent.dll" ()

Private m_CodeDebug As Boolean  ' True -> show NAD/NGVD I/O
Dim m_NadInitialized As Boolean

Dim m_ierr As Long  ' Error flag: 0 -> No error

' Event     <><><><><><><><><><><><><><><><><><><><><><><><>
' Name:     Class_Initialize
' Purpose:  Sets the value of m_NadInitialized to False
'
Private Sub Class_Initialize()
  m_NadInitialized = False
  m_CodeDebug = False
End Sub

' Function  - - - - - - - - - - - - - - - - - - - - - - - - -
' Name:     Initialize
' Purpose:  Initializes the error-handler class and
'           required NAD datum-conversion libraries.
'           Returns "False" if initialization fails.
'
' Notes:    Separating this function into two halves --
'           one for initializing LAT-LONG conversion and
'           another for initializing ALTITUDE conversion
'           can result in a fatal shutdown if both halves
'           attempt to open the same error file.
'
'           Therefore, one initialization function will
'           open both sets of conversion libraries.
'
Public Function Initialize() As Boolean

  On Error GoTo errInitialize

  If m_NadInitialized = True Then
    Initialize = True
    Exit Function
  End If

  ' Reinitialize the error handler:
  m_objErrors.Clear
  
  ' - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  ' Initialize NAD libraries and open NAD error file:
  ' - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  Call F90_W99OPN
  
  ' - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  ' Initialize Fortran LAT-LONG conversion routines:
  ' - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  Dim aname As String      ' <- Location of NAD datum files

  aname = "c:\Swuds\Support\NadCon\Data\area.par"
'  aname = "e:\swuds\src\NadCon\Data\area.par"
  Call F90_NADINIT(m_ierr, aname, Len(aname))

  If m_ierr <> 0 Then
    m_objErrors.Add 2000, "F", 0, "modNadCon.Initialize", _
    "FATAL ERROR: PROBLEM INITIALIZING LATITUDE-LONGITUDE ROUTINES !"
    GoTo errInitialize
  End If

  ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  ' Initialize Fortran altitude-correction routines:
  ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  Call F90_VERTINIT(m_ierr)
    
  If m_ierr <> 0 Then
    m_objErrors.Add 2000, "F", 0, "modNadCon.Initialize", _
    "FATAL ERROR: PROBLEM INITIALIZING DATUM CONVERSION-CORRECTION ROUTINES !"
    GoTo errInitialize
  End If
      
  ' - - - - - - - - - - - - - - - - - - - - - - -
  ' NAD library routines have been initialized.
  ' - - - - - - - - - - - - - - - - - - - - - - -
  Initialize = True
  m_NadInitialized = True
  Exit Function

errInitialize:
  Initialize = False
End Function

' Subroutine ===============================================
' Name:      ConvertNAD (7 arguments)
' Purpose:   Converts latitude-longitude input values to
'            NAD-shifted output values.
'
Public Sub ConvertNAD( _
           ToDatm As Integer, _
           LatDMS As String, _
           LonDMS As String, _
           ShfLatDMS As String, _
           ShfLonDMS As String, _
           ShfLatDec As String, _
           ShfLonDec As String, _
           Ierr As Long)

  On Error GoTo errConvertNAD

  ' - - - - - - - - - - - - - - - - -
  ' Reinitialize the error handler:
  ' - - - - - - - - - - - - - - - - -
  m_objErrors.Clear

  Dim InLatDec As Double    ' <- Input to Fortran routines
  Dim InLonDec As Double    ' <- Input to Fortran routines

  Dim OutLatDec As Double   ' <- Output from Fortran routines
  Dim OutLonDec As Double   ' <- Output from Fortran routines

  Dim LatND As Long         ' <- Latitude, number of decimal places
  Dim LonND As Long         ' <- Longitude, number of decimal places
  
  Dim Deg As Long           ' <- local processing variable
  Dim Min As Long           ' <- local processing variable
  Dim Sec As Double         ' <- local processing variable

  ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  ' Parse and verify D-M-S latitude; convert to decimal degrees:
  ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  ParseDMS LatDMS, 1, Deg, Min, Sec, LatND, m_ierr
  If m_ierr = 0 Then
    ConvertUnits 1, Deg, Min, Sec, InLatDec
  Else
    m_objErrors.Add 2000, "F", 0, "modNadCon.ConvertNAD", _
    "FATAL ERROR: PROBLEM IN D-M-S LATITUDE: " & LatDMS
    GoTo errConvertNAD
  End If

  ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  ' Parse and verify D-M-S longitude; convert to decimal degrees:
  ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  ParseDMS LonDMS, 2, Deg, Min, Sec, LonND, m_ierr
  If m_ierr = 0 Then
    ConvertUnits 1, Deg, Min, Sec, InLonDec
  Else
    m_objErrors.Add 2000, "F", 0, "modNadCon.ConvertNAD", _
    "FATAL ERROR: PROBLEM PARSING D-M-S LONGITUDE: " & LonDMS
    GoTo errConvertNAD
  End If

  If ToDatm = 1 Then
    ' +--------------------------------------------------------------+
    ' |  CONVERT DATUM FROM NAD83 TO NAD27  ->  Ftn input flag = -1  |
    ' +--------------------------------------------------------------+

    ' - - - - - - - - - - - - - - - - - - - - - - - - - - -
    ' Calculate datum shift using values parsed from D-M-S:
    ' - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Call F90_NADCON(-1, InLonDec, InLatDec, OutLonDec, OutLatDec, m_ierr)
    If m_ierr <> 0 Then
      m_objErrors.Add 2000, "F", 0, "modNadCon.ConvertNAD", _
      "FATAL ERROR: PROBLEM CONVERTING D-M-S INPUT TO NAD 1927."
      GoTo errConvertNAD
    End If

    ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    ' Convert shifted datum coords to D-M-S and assemble output strings:
    ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Call F90_HMS(OutLatDec, Deg, Min, Sec)
    ShfLatDMS = LeftPad(CStr(Deg), 2, "0") & _
                LeftPad(CStr(Min), 2, "0") & _
                LeftPad(CStr(modRound.RoundDc(Sec, LatND)), 2, "0")
    
    Call F90_HMS(OutLonDec, Deg, Min, Sec)
    ShfLonDMS = LeftPad(CStr(Deg), 2, "0") & _
                LeftPad(CStr(Min), 2, "0") & _
                LeftPad(CStr(modRound.RoundDc(Sec, LonND)), 2, "0")
                
    ' - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    ' Convert output in decimal degrees to strings:
    ' - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    ShfLatDec = CStr(modRound.RoundDc(OutLatDec, 4 + LatND))
    ShfLonDec = CStr(modRound.RoundDc(OutLonDec, 4 + LonND))

  Else

    ' +--------------------------------------------------------------+
    ' |  CONVERT DATUM FROM NAD27 TO NAD83  ->  Ftn input flag = +1  |
    ' +--------------------------------------------------------------+

    ' - - - - - - - - - - - - - - - - - - - - - - - - - - -
    ' Calculate datum shift using values parsed from D-M-S:
    ' - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Call F90_NADCON(1, InLonDec, InLatDec, OutLonDec, OutLatDec, m_ierr)
    If m_ierr <> 0 Then
      m_objErrors.Add 2000, "F", 0, "modNadCon.ConvertNAD", _
      "FATAL ERROR: PROBLEM CONVERTING D-M-S INPUT TO NAD 1983."
      GoTo errConvertNAD
    End If

    ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    ' Convert shifted datum coords to D-M-S and assemble output strings:
    ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Call F90_HMS(OutLatDec, Deg, Min, Sec)
    ShfLatDMS = LeftPad(CStr(Deg), 2, "0") & _
                LeftPad(CStr(Min), 2, "0") & _
                LeftPad(CStr(modRound.RoundDc(Sec, LatND)), 2, "0")
    
    Call F90_HMS(OutLonDec, Deg, Min, Sec)
    ShfLonDMS = LeftPad(CStr(Deg), 2, "0") & _
                LeftPad(CStr(Min), 2, "0") & _
                LeftPad(CStr(modRound.RoundDc(Sec, LonND)), 2, "0")

    ' - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    ' Convert output in decimal degrees to strings:
    ' - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    ShfLatDec = CStr(modRound.RoundDc(OutLatDec, 4 + LatND))
    ShfLonDec = CStr(modRound.RoundDc(OutLonDec, 4 + LonND))

  End If

  Ierr = m_ierr
  ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  ' On debug, display datum conversion I/O in a message box:
  ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  If m_CodeDebug Then
    MsgBox _
  "     COORDINATE CORRECTION" & vbCrLf & _
  "---------------------------------------------------------" & vbCrLf & _
  "                D - M - S        Dec Deg" & vbCrLf & _
  "---------------------------------------------------------" & vbCrLf & _
  "In Lat:     " & LatDMS & "  ->  " & CStr(modRound.RoundDc(InLatDec, 4 + LatND)) & vbCrLf & _
  "In Lon:    " & LonDMS & "  ->  " & CStr(modRound.RoundDc(InLonDec, 4 + LonND)) & vbCrLf & _
  "---------------------------------------------------------" & vbCrLf & _
  "Out Lat:  " & ShfLatDMS & "  <-  " & ShfLatDec & vbCrLf & _
  "Out Lon: " & ShfLonDMS & "  <-  " & ShfLonDec & vbCrLf & _
  "---------------------------------------------------------" & vbCrLf & _
  "Diff Lat:                         " & CStr(modRound.RoundDc((InLatDec - OutLatDec), 4 + LatND)) & vbCrLf & _
  "Diff Lon:                        " & CStr(modRound.RoundDc((InLonDec - OutLonDec), 4 + LonND)) & vbCrLf & _
  "---------------------------------------------------------"
  End If
  Exit Sub

errConvertNAD:
  MsgBox "NAD ERROR CODE = " & m_ierr, vbExclamation
  Ierr = m_ierr

End Sub

' Subroutine ===============================================
' Name:      ConvertNGVD (5 arguments)
' Purpose:   Calculates an altitude-correction value
'            in meters NGVD for a specified location.
'
Public Sub ConvertNGVD( _
           ToDatm As Integer, _
           LatDMS As String, _
           LonDMS As String, _
           InAlt As String, _
           OutAlt As String, _
           Ierr As Long)

  On Error GoTo errConvertNGVD

  ' - - - - - - - - - - - - - - - - -
  ' Reinitialize the error handler:
  ' - - - - - - - - - - - - - - - - -
  m_objErrors.Clear

  Dim InLatDec As Double    ' <- Input to Fortran routine
  Dim InLonDec As Double    ' <- Input to Fortran routine
  Dim AltCor As Single      ' <- Output from Fortran routine

  Dim LatND As Long         ' <- Latitude, number of decimal places
  Dim LonND As Long         ' <- Longitude, number of decimal places
  Dim AltND As Long         ' <- Altitude, number of decimal places

  Dim Deg As Long           ' <- local processing variable
  Dim Min As Long           ' <- local processing variable
  Dim Sec As Double         ' <- local processing variable
  Dim Lstr As Long          ' <- local processing variable
  Dim pdec As Long          ' <- local processing variable

  ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  ' Parse and verify DMS latitude; convert it to decimal degrees:
  ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  ParseDMS LatDMS, 1, Deg, Min, Sec, LatND, m_ierr
  If m_ierr = 0 Then
    ConvertUnits 1, Deg, Min, Sec, InLatDec
  Else
    m_objErrors.Add 2000, "F", 0, "modNadCon.ConvertNGVD", _
    "FATAL ERROR: PROBLEM IN D-M-S LATITUDE: " & LatDMS
    GoTo errConvertNGVD
  End If

  ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  ' Parse and verify DMS longitude; convert it to decimal degrees:
  ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  ParseDMS LonDMS, 2, Deg, Min, Sec, LonND, m_ierr
  If m_ierr = 0 Then
    ConvertUnits 1, Deg, Min, Sec, InLonDec
  Else
    m_objErrors.Add 2000, "F", 0, "modNadCon.ConvertNGVD", _
    "FATAL ERROR: PROBLEM PARSING D-M-S LONGITUDE: " & LonDMS
    GoTo errConvertNGVD
  End If

  ' - - - - - - - - - - - - - - - - - - - - - - - - - - -
  ' Calculate the altitude correction value:
  ' - - - - - - - - - - - - - - - - - - - - - - - - - - -
  Call F90_VERTCON(InLonDec, InLatDec, AltCor, m_ierr)

  If m_ierr <> 0 Then
    m_objErrors.Add 2000, "F", 0, "modNadCon.ConvertNAD", _
    "FATAL ERROR: PROBLEM CALCULATING ALTITUDE CORRECTION."
    GoTo errConvertNGVD
  End If

  ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  ' Transform altitude correction value from meters to feet:
  ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  AltCor = 3.2808 * AltCor

  ' - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  ' Adjust algebraic sign for NGVD conversion direction:
  ' - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  If ToDatm = 1 Then AltCor = -AltCor

  ' - - - - - - - - - - - - - - - - - - - - - - -
  ' Apply the altitude correction:
  ' - - - - - - - - - - - - - - - - - - - - - - -
  InAlt = Trim(InAlt)
  Lstr = Len(InAlt)

  ' - - - - - - - - - - - - - - - - - - - - - - -
  ' Find position of the decimal point, if any:
  ' - - - - - - - - - - - - - - - - - - - - - - -
  pdec = InStr(1, InAlt, ".", vbTextCompare)
  If pdec = 0 Then InAlt = InAlt & "."
  
  ' - - - - - - - - - - - - - - - - - - - - - - -
  ' Determine the number of decimal places:
  ' - - - - - - - - - - - - - - - - - - - - - - -
  AltND = 0
  If pdec > 0 Then AltND = Lstr - pdec

  OutAlt = CStr(modRound.RoundDc(CSng(Val(InAlt)) + AltCor, AltND))
  Ierr = m_ierr

  ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  ' On debug, display altitude datum correction I/O in a message box:
  ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  
  If m_CodeDebug Then
    If ToDatm = 1 Then
      MsgBox _
  "        ALTITUDE CORRECTION" & vbCrLf & _
  "---------------------------------------------------------" & vbCrLf & _
  "                D - M - S        Dec Deg" & vbCrLf & _
  "---------------------------------------------------------" & vbCrLf & _
  "In Lat:     " & LatDMS & "  ->  " & CStr(modRound.RoundDc(InLatDec, 4 + LatND)) & vbCrLf & _
  "In Lon:    " & LonDMS & "  ->  " & CStr(modRound.RoundDc(InLonDec, 4 + LonND)) & vbCrLf & _
  "============================" & vbCrLf & _
  "In Alt NGVD 1988:        " & InAlt & vbCrLf & _
  "Alt correction:                 " & CStr(modRound.RoundDc(AltCor, 4)) & vbCrLf & _
  "Out Alt NGVD 1929:      " & OutAlt & vbCrLf & _
  "---------------------------------------------------------"
    Else
      MsgBox _
  "        ALTITUDE CORRECTION" & vbCrLf & _
  "---------------------------------------------------------" & vbCrLf & _
  "                D - M - S        Dec Deg" & vbCrLf & _
  "---------------------------------------------------------" & vbCrLf & _
  "In Lat:     " & LatDMS & "  ->  " & CStr(modRound.RoundDc(InLatDec, 4 + LatND)) & vbCrLf & _
  "In Lon:    " & LonDMS & "  ->  " & CStr(modRound.RoundDc(InLonDec, 4 + LonND)) & vbCrLf & _
  "============================" & vbCrLf & _
  "In Alt NGVD 1929:        " & InAlt & vbCrLf & _
  "Alt correction:                 " & CStr(modRound.RoundDc(AltCor, 4)) & vbCrLf & _
  "Out Alt NGVD 1988:      " & OutAlt & vbCrLf & _
  "---------------------------------------------------------"
    End If
  End If
  
  Exit Sub

errConvertNGVD:
  MsgBox "NGVD ERROR CODE = " & m_ierr, vbExclamation
  Ierr = m_ierr

End Sub

' Subroutine ===============================================
' Name:      ParseDMS (7 arguments)
' Purpose:   Subdivides DMS string into degrees, minutes,
'            and seconds strings
'
Private Sub ParseDMS(DMS As String, _
                     LatLon As Integer, _
                     Deg As Long, _
                     Min As Long, _
                     Sec As Double, _
                     Ndec As Long, _
                     Ierr As Long)

  Dim j As Integer
  Dim Lstr As Integer
  Dim test As String
  Dim sign As String
  Dim pdec As Integer
  Dim psec As Integer

  ' - - - - - - - - - - - - - - - - - - - -
  ' Remove any leading or trailing spaces:
  ' - - - - - - - - - - - - - - - - - - - -
  DMS = Trim(DMS)
  Lstr = Len(DMS)
  If Lstr < 1 Then
    Ierr = 1
    Exit Sub
  End If

  ' - - - - - - - - - - - - - - - - - - - - - -
  ' Check for less than minimum length:
  ' - - - - - - - - - - - - - - - - - - - - - -
  If LatLon = 1 Then
    If Lstr < 6 Then
      Ierr = 2
      Exit Sub
    End If
  Else
    If Lstr < 7 Then
      Ierr = 2
      Exit Sub
    End If
  End If

  ' - - - - - - - - - - - - - - - - - - - - - -
  ' Scan DMS string for non-numeric characters:
  ' - - - - - - - - - - - - - - - - - - - - - -
  If Not IsNumeric(DMS) Then
    Ierr = 3
    Exit Sub
  End If
  
  ' - - - - - - - - - - - - - - - - - - - - - -
  ' Check for an algebraic sign:
  ' - - - - - - - - - - - - - - - - - - - - - -
  If Left(DMS, 1) = "+" Or Left(DMS, 1) = "-" Then
    sign = Left(DMS, 1)
    DMS = Mid(DMS, 2)
    Lstr = Lstr - 1
  End If

  ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  ' Find position in string of the decimal point, if any (pdec):
  ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  pdec = InStr(1, DMS, ".", vbTextCompare)
  
  ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  ' Calc number of decimal places in the input string (Ndec):
  ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  If pdec <= 0 Then
    Ndec = 0
  Else
    Ndec = Lstr - pdec
  End If

  ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  ' Calc position in string of beginning of seconds field (psec):
  ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  If Ndec > 0 Then
    psec = pdec - 2
  Else
    psec = Lstr - 1
  End If

  ' - - - - - - - - - - - - - - - - - - - - - - - - -
  ' Subdivide D-M-S string into D, M, S values
  ' Seconds = string from psec to right-hand end:
  ' - - - - - - - - - - - - - - - - - - - - - - - - -
  Sec = CDbl(Mid(DMS, psec))
  
  ' - - - - - - - - - - - - - - - - - - - - - - - - -
  ' Minutes = 2 characters from psec - 2 to psec - 1:
  ' - - - - - - - - - - - - - - - - - - - - - - - - -
  Min = Int(Mid(DMS, psec - 2, 2))
  
  ' - - - - - - - - - - - - - - - - - - - - - - - - -
  ' Degrees = left side of string psec-3 characters:
  ' - - - - - - - - - - - - - - - - - - - - - - - - -
  Deg = Int(Left(DMS, psec - 3))

  If sign = "-" Then Deg = -1 * Deg
  
  ' - - - - - - - - - - - - - - - - - - - -
  ' Verify that degrees value is in range.
  ' Latitude and longitude are different:
  ' - - - - - - - - - - - - - - - - - - - -
  If LatLon = 1 Then
    CheckNumber CStr(Deg), True, True, "-90", "90", Ierr
  Else
    CheckNumber CStr(Deg), True, True, "-180", "180", Ierr
  End If

  ' - - - - - - - - - - - - - - - - - - - -
  ' Verify that minutes value is in range:
  ' - - - - - - - - - - - - - - - - - - - -
  CheckNumber CStr(Min), True, False, "0", "59", Ierr
    
  ' - - - - - - - - - - - - - - - - - - - -
  ' Verify that seconds value is in range:
  ' - - - - - - - - - - - - - - - - - - - -
  If pdec = 0 Then
    CheckNumber CStr(Sec), True, False, "0", "59", Ierr
  Else
    CheckNumber CStr(Sec), False, False, "0.0", "59.9999", Ierr
  End If

End Sub

' Subroutine ===============================================
' Name:      LeftPad (3 arguments)
' Purpose:   Left-pads a string with a specified character
'            to a specified width.
'
'            If string is recognizable as a signed number,
'            sign is preserved in the first position.
'
Private Function LeftPad(str As String, _
                         wid As Long, _
                Optional pad As String = " ") As String

  Dim Lstr As Long
  Dim pdec As Long
  Dim sign As String
  Dim valu As String
  Dim whol As String
  Dim frac As String

  str = Trim(str)
  Lstr = Len(str)
  
   ' - - - - - - - - - - - - - - - - - - - - - -
   ' Can input string be recognized as a number?
   ' - - - - - - - - - - - - - - - - - - - - - -
  If IsNumeric(str) = True Then
    '  - - - - - - - - - - - - - - - - - - - -
    '  Does the number have an algebraic sign?
    '  - - - - - - - - - - - - - - - - - - - -
    If Left(str, 1) Like "[+-]" Then
      ' - - - - - - - - - - - - - -
      ' Yes - the number has a sign
      ' - - - - - - - - - - - - - -
      sign = Left(str, 1)
      valu = Mid(str, 2)
      Lstr = Len(valu)
      ' - - - - - - - - - - - - - - - - - - - - - - - -
      ' Does the signed number contain a decimal point?
      ' - - - - - - - - - - - - - - - - - - - - - - - -
      pdec = InStr(1, valu, ".", vbTextCompare)
      If pdec > 0 Then
        ' - - - - - - - - - -
        ' Yes - decimal point
        ' - - - - - - - - - -
        whol = Left(valu, pdec - 1)
        frac = Mid(valu, pdec)
        Lstr = Len(whol)
        Do While Lstr < wid
          whol = pad & whol
          Lstr = Len(whol)
        Loop
        LeftPad = sign & whol & frac
      Else
        ' - - - - - - - - - -
        ' No - decimal point
        ' - - - - - - - - - -
        Do While Lstr < wid
          valu = pad & valu
          Lstr = Len(valu)
        Loop
        LeftPad = sign & valu
      End If
      Exit Function
    End If
    ' - - - - - - - - - - - - - -
    ' No - the number has no sign
    ' - - - - - - - - - - - - - -
    pdec = InStr(1, str, ".", vbTextCompare)
    If pdec > 0 Then
      whol = Left(str, pdec - 1)
      frac = Mid(str, pdec)
      Lstr = Len(whol)
      Do While Lstr < wid
        whol = pad & whol
        Lstr = Len(whol)
      Loop
      LeftPad = whol & frac
      Exit Function
    End If
  End If
  ' - - - - - - - - - - - - - - - - - -
  ' Left padding for unsigned integers
  ' and for non-numeric strings:
  ' - - - - - - - - - - - - - - - - - -
  Do While Lstr < wid
    str = pad & str
    Lstr = Len(str)
  Loop
  LeftPad = str
End Function

' Subroutine ===============================================
' Name:      CheckNumber (6 arguments)
' Purpose:   Checks input string for valid numbers.
'
Private Sub CheckNumber(str As String, _
                        IntFlg As Boolean, _
                        NegFlg As Boolean, _
                        CMin As String, _
                        CMax As String, _
                        Ierr As Long)

  Dim Lstr As Long
  Lstr = Len(Trim(str))

  ' - - - - - - - - - - - - - - - - - -
  '  ERROR #1: String cannot be blank:
  ' - - - - - - - - - - - - - - - - - -
  If Lstr < 1 Then
    Ierr = 1
    m_objErrors.Add 2000, "F", 0, "modNadCon.CheckNumber", _
    "ERROR: THIS FIELD CANNOT BE BLANK !"
    GoTo errInInput
  End If

  ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  '  ERROR #2: String cannot contain non-numeric characters:
  ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  If IsNumeric(str) = False Then
    Ierr = 2
    m_objErrors.Add 2000, "F", 0, "modNadCon.CheckNumber", _
    "ERROR: FIELD CONTAINS NON-NUMERIC CHARACTERS: " & Trim(str)
    GoTo errInInput

  Else
   
  ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  '  ERROR #3: Strings representing integers cannot contain a decimal point:
  ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    If IntFlg = True Then
      Dim j As Long
      For j = 1 To Lstr
         If Mid(str, j, 1) = "." Then
           Ierr = 3
           m_objErrors.Add 2000, "F", 0, "modNadCon.CheckNumber", _
           "ERROR: THIS FIELD MUST BE AN INTEGER !"
           GoTo errInInput
         End If
      Next
    End If

  ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  '  ERROR #4: String representing positive numbers cannot contain "-":
  ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    If NegFlg = False Then
      If Left(str, 1) = "-" Then
        Ierr = 4
        m_objErrors.Add 2000, "F", 0, "modNadCon.CheckNumber", _
        "ERROR: THIS FIELD CANNOT BE NEGATIVE !"
        GoTo errInInput
      End If
    End If
  
  ' - - - - - - - - - - - - - - - - - - - - - - - - - -
  '  ERROR #5: Value cannot be out of specified range:
  ' - - - - - - - - - - - - - - - - - - - - - - - - - -
    If Val(Trim(str)) < Val(CMin) Or Val(Trim(str)) > Val(CMax) Then
      Ierr = 5
      m_objErrors.Add 2000, "F", 0, "modNadCon.CheckNumber", _
      "ERROR: VALUE OUT OF RANGE: " & CMin & " to " & CMax
      GoTo errInInput
    End If
  End If

  Ierr = 0
  Exit Sub

errInInput:
  
End Sub

' Subroutine ===============================================
' Name:      ConvertUnits (5 arguments)
' Purpose:   Converts D-M-S values to decimal degrees
'            and decimal-degrees value to D-M-S.
'
Private Sub ConvertUnits(Flag As Integer, _
                          Deg As Long, _
                          Min As Long, _
                          Sec As Double, _
                          Dec As Double)
  Dim sign As Integer
  If Flag < 0 Then
    ' - - - - - - - - - - - - - - - - - - -
    ' Convert decimal-degrees to D, M, S:
    ' - - - - - - - - - - - - - - - - - - -
    sign = Sgn(Dec)
    If sign < 0 Then Dec = Abs(Dec)
    Deg = Int(Dec)
    Min = Int((Dec - CDbl(Deg)) * 60#)
    If Min > 59 Then
      Deg = Deg + 1
      Min = Min - 60
    End If
    Sec = ((Dec - CDbl(Deg)) * 60#) * 60# - CDbl(Min * 60#)
    If Sec >= 60 Then
      Min = Min + 1
      Sec = Sec - 60#
    End If
   Else
    ' - - - - - - - - - - - - - - - - - - -
    ' Convert D, M, S to decimal-degrees:
    ' - - - - - - - - - - - - - - - - - - -
    sign = Sgn(Deg)
    If sign < 0 Then Deg = Abs(Deg)
    Dec = CDbl(Deg) + CDbl(Min) / 60# + CDbl(Sec) / 3600#
    Dec = CDbl(sign) * Dec
  End If
End Sub

' =============================================================
'  PROPERTY SETTINGS AFTER THIS POINT ...
' =============================================================

' Property + + + + + + + + + + + + + + + + + + + + + + + + +
' Name:    Get objErrors
' Purpose: Object that handles all error processing.
'
Public Property Get objErrors() As ErrorHandler.clsErrors
  On Error GoTo objErrorsGetErr

  Set objErrors = m_objErrors
  Exit Property

objErrorsGetErr:
'  Call RaiseError(9999, "clsNadCon:objErrors Property Get")
End Property

' Property + + + + + + + + + + + + + + + + + + + + + + + + +
' Name:    Set objErrors (1 argument)
' Purpose: Object that handles all error processing.
'
Public Property Set objErrors(ByVal NewobjErrors As ErrorHandler.clsErrors)
 ' The property value can only be set once.
   If m_objErrors Is Nothing Then      ' Assign the initial value.
       Set m_objErrors = NewobjErrors
   Else
       Err.Raise Number:=vbObjectError + 32144, _
       Description:="objErrors could not be set"
   End If
End Property

' Property + + + + + + + + + + + + + + + + + + + + + + + + +
' Name:    Let CodeDebug (1 argument)
' Purpose: Toggles debug features On/Off.
'
Public Property Let CodeDebug(ByVal Setting As Boolean)
  Let m_CodeDebug = Setting
End Property

