unit uLatLonEntry;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Buttons, ComCtrls, ExtCtrls;

type
  TfLatLon = class(TForm)
    eDMS_LatD: TEdit;
    Label1: TLabel;
    eDMS_LatM: TEdit;
    eDMS_LatS: TEdit;
    eNS: TEdit;
    eDMS_LonD: TEdit;
    eDMS_LonM: TEdit;
    eDMS_LonS: TEdit;
    eEW: TEdit;
    eDM_LatD: TEdit;
    eDM_LatM: TEdit;
    eDM_LonD: TEdit;
    eDM_LonM: TEdit;
    Label2: TLabel;
    eD_LatD: TEdit;
    eD_LonD: TEdit;
    Label3: TLabel;
    btOK: TBitBtn;
    btCancel: TBitBtn;
    eElv: TEdit;
    Label4: TLabel;
    Label5: TLabel;
    sVINV: TShape;
    Label6: TLabel;
    lVINV: TLabel;
    procedure LoadFormLatLon( Lat, Lon: extended );
    procedure LoadForm( Lat, Lon, Elv: extended );
    procedure eElvExit(Sender: TObject);
    procedure eDMS_LatDExit(Sender: TObject);
    procedure eDM_LatDExit(Sender: TObject);
    procedure CheckValid;
    procedure eD_LatDExit(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  fLatLon: TfLatLon;

  function EditLatLonElv( Title: string; var sLL: string ): integer;

implementation

uses uUtility;

{$R *.dfm}

procedure D2DM( D: extended; var dd, mm: extended );
begin
  dd := Trunc( D );
  mm := Frac( D ) * 60.0;
end;

procedure D2DMS( D: extended; var dd, mm, ss: extended );
begin
  D2DM( D, dd, mm );
  ss := Frac( mm ) * 60.0;
  mm := Trunc( mm );
end;

procedure DecodeDM( S: string; var _Lat, _Lon, _Elv: extended );
// S = 'ddMM.mmmmmmm,n,dddMM.mmmmmmm,e,EEEE.eee'
var
  T,U: string;
  M: extended;
begin
  U := Copy( S, 1, 2 );
  Delete( S, 1, 2 );
  T := TokenParse( S, ',' );
  _Lat := Str2Ext( U ) + Str2Ext( T ) / 60.0;

  T := TokenParse( S, ',' );
  if (FirstChar(T) IN['S','s','-']) then _Lat := -_Lat;

  U := Copy( S, 1, 3 );
  Delete( S, 1, 3 );
  T := TokenParse( S, ',' );
  _Lon := Str2Ext( U ) + Str2Ext( T ) / 60.0;
  T := TokenParse( S, ',' );
  if (FirstChar(T) IN['W','w','-']) then _Lon := -_Lon;
  T := TokenParse( S, ',' );
  _Elv := Str2Ext( T );
end;

procedure TfLatLon.LoadFormLatLon( Lat, Lon: extended );
var
  D,M,S: extended;
begin
  // NS EW
  if Lat > 0 then eNS.Text := 'N' else eNS.Text := 'S';
  if Lon > 0 then eEW.Text := 'E' else eEW.Text := 'W';
  // D
  eD_LatD.Text := Ext2Str( abs(Lat), 9 );
  eD_LonD.Text := Ext2Str( abs(Lon), 10 );
  // DM
  D2DM( abs(Lat), D, M );
  eDM_LatD.Text := IntToStr( Trunc( D ) );
  eDM_LatM.Text := Ext2Str( M, 7 );
  D2DM( abs(Lon), D, M );
  eDM_LonD.Text := IntToStr( Trunc( D ) );
  eDM_LonM.Text := Ext2Str( M, 7 );
  // DMS
  D2DMS( abs(Lat), D, M, S );
  eDMS_LatD.Text := IntToStr( Trunc( D ) );
  eDMS_LatM.Text := IntToStr( Trunc( M ) );
  eDMS_LatS.Text := Ext2Str( S, 5 );
  D2DMS( abs(Lon), D, M, S );
  eDMS_LonD.Text := IntToStr( Trunc( D ) );
  eDMS_LonM.Text := IntToStr( Trunc( M ) );
  eDMS_LonS.Text := Ext2Str( S, 5 );
end;

procedure TfLatLon.LoadForm( Lat, Lon, Elv: extended );
begin
  // Load the edit boxes
  // Elv
  eElv.Text := Ext2Str( Elv, 3 );
  // Lat/Lon
  LoadFormLatLon( Lat, Lon );
end;

function EditLatLonElv( Title: string; var sLL: string ): integer;
var
  Lat, Lon, Elv: extended;
begin
  with fLatLon do
  begin
    Caption := Title;
    DecodeDM( sLL, Lat, Lon, Elv );
    LoadForm( Lat, Lon, Elv );
    result := ShowModal;
    if result = mrOK then
      sLL := ZeroPad( eDM_LatD.Text, 2 ) + eDM_LatM.Text + ',' + eNS.Text + ',' +
             ZeroPad( eDM_LonD.Text, 3 ) + eDM_LonM.Text + ',' + eEW.Text + ',' +
             eElv.Text;
  end;
end;


procedure TfLatLon.eElvExit(Sender: TObject);
// User leaves elevation
var
  S: string;
  f: extended;
begin
  S := Trim( eElv.Text );
  eElv.Text := KeepInString( eElv.Text, '1234567890.-' );
  f := Str2Ext( eElv.Text );
  if LastChar( S ) in ['F','f'] then
  begin
    f := f * 0.30480061;
  end;
  eElv.Text := Ext2Str( f, 3 );
  CheckValid;
end;

procedure TfLatLon.CheckValid;
var
  S: string;
  Lat, Lon, Elv: extended;
  Valid: boolean;
begin
  S := ZeroPad( eDM_LatD.Text, 2 ) + eDM_LatM.Text + ',' + eNS.Text + ',' +
                                    ZeroPad( eDM_LonD.Text, 3 ) + eDM_LonM.Text + ',' + eEW.Text + ',' +
                                     eElv.Text;
  DecodeDM( S, Lat, Lon, Elv );
  Valid := fInRange( Lat, -90.0, +90.0 )
           AND fInRange( Lon, -180.0, +180.0 )
           AND fInRange( Elv, -200.0, 10000.0 );
  if Valid then
  begin
    lVINV.Caption := 'VALID';
    sVINV.Brush.Color := clLime;
  end else
  begin
    lVINV.Caption := 'INVALID';
    sVINV.Brush.Color := clRed;
  end;
end;

procedure TfLatLon.eDMS_LatDExit(Sender: TObject);
// User leaves one of the DMS edit boxes
var
  Lat, Lon, dd, mm: extended;
begin
  try
    Lat := StrToFloat( eDMS_LatD.Text ) + StrToFloat( eDMS_LatM.Text ) / 60.0 + StrToFloat( eDMS_LatS.Text ) / 3600.0;
    if FirstChar( eNS.Text ) in ['S','s','-'] then Lat := -Lat;
    Lon := StrToFloat( eDMS_LonD.Text ) + StrToFloat( eDMS_LonM.Text ) / 60.0 + StrToFloat( eDMS_LonS.Text ) / 3600.0;
    if FirstChar( eEW.Text ) in ['W','s','-'] then Lon := -Lon;
    LoadFormLatLon( Lat, Lon );
  except
  end;
  CheckValid;
end;

procedure TfLatLon.eD_LatDExit(Sender: TObject);
var
  Lat, Lon, dd, mm: extended;
begin
  try
    Lat := StrToFloat( eD_LatD.Text );
    if FirstChar( eNS.Text ) in ['S','s','-'] then Lat := -Lat;
    Lon := StrToFloat( eD_LonD.Text );
    if FirstChar( eEW.Text ) in ['W','s','-'] then Lon := -Lon;
    LoadFormLatLon( Lat, Lon );
  except
  end;
  CheckValid;
end;

procedure TfLatLon.eDM_LatDExit(Sender: TObject);
// DM entry box has been changed.
var
  Lat, Lon, dd, mm: extended;
begin
  try
    Lat := StrToFloat( eDM_LatD.Text ) + StrToFloat( eDM_LatM.Text ) / 60.0;
    if FirstChar( eNS.Text ) in ['S','s','-'] then Lat := -Lat;
    Lon := StrToFloat( eDM_LonD.Text ) + StrToFloat( eDM_LonM.Text ) / 60.0;
    if FirstChar( eEW.Text ) in ['W','s','-'] then Lon := -Lon;
    LoadFormLatLon( Lat, Lon );
  except
  end;
  CheckValid;
end;


end.
