#!/bin/perl
# dms2ll.pl - Oct 2005 - geoff mclane
# AIM: To accept an input DEGREES MINUTES (SECONDS DECIMAL) [NSEW]
# and convert to a single decimal degrees ... 47 28 0 N
# 28/03/2014 - some improvements
# 13/03/2014 - some more improvements BUT STILL VERY UGLY ;=((
# 24/09/2013 - some improvements
# 21/07/2011 - Fix bug when entered 
# 12/11/2010 - Attempt to reconstitute the same FORM
# 20061206 - Later added conversion back to DEG MIN SECS as a check ...
# Mt Everest 27 59 17 N = 27.9880555555556 - 86 55 31 E = 86.9252777777778 = N27E086.hgt
use strict;
use warnings;
# print "Hello, World ... Arguments are ...\n";
my $g_argcnt = 0;
my $arg = '';
my $arg2 = '';
my $g_args = '';  # out of array, into string
my $degs = 0;
my $deg1 = 0;
my $quad = '';
my $g_degs = 0; # first argment

# functions
my $sdeg = ' ';

# options
my $add_reverse_calc = 0;

sub prt($) { printf shift; }

sub deg2dms2f {
    my ($pos, $neg, $deg) = @_;

    my $sign = $pos;
    if ($deg < 0) {
        $sign = $neg;
        $deg = -$deg;
    }
    my $d = int($deg * 360000 + 0.5);
    my $s = ($d % 6000) / 100;
    $d = int($d / 6000);
    my $m = $d % 60;
    $d = int($d / 60);

    return ($sign, $d, $m, $s);
}

sub deg2dm {
    my ($pos, $neg, $deg) = @_;

    my $sign = $pos;
    if ($deg < 0) {
        $sign = $neg;
        $deg = -$deg;
    }
    my $d = int($deg * 60 + 0.5);
    my $m = $d % 60;
    $d = int($d / 60);

    return ($sign, $d, $m);
}

sub coords2str {
    my ($lat, $lon) = @_;
    my $form ="%s%02d${sdeg}%02d'%05.2f\", %s%03d${sdeg}%02d'%05.2f\" (%.7f, %.7f)"; 
    sprintf($form,
            deg2dms2f('N', 'S', $lat), deg2dms2f('E', 'W', $lon), $lat, $lon);
}

sub lat2str {
    my ($lat) = @_;
    my $form ="%s%02d${sdeg}%02d'%05.2f\" (%.7f)"; 
    sprintf($form, deg2dms2f('N', 'S', $lat), $lat );
}

sub lon2str {
    my ($lon) = @_;
    my $form ="%s%02d${sdeg}%02d'%05.2f\" (%.7f)"; 
    sprintf($form, deg2dms2f('E', 'W', $lon), $lon );
}

sub lon2strdm {
    my ($md) = @_;
    my $form = "%s%d${sdeg}%02d' (%.2f)";
    sprintf($form, deg2dm('E', 'W', $md), $md);
}

sub lat2strdm {
    my ($md) = @_;
    my $form = "%s%d${sdeg}%02d' (%.2f)";
    sprintf($form, deg2dm('N', 'S', $md), $md);
}

sub give_help() {
    prt("\nHELP: Give quadrant, N, S, E, W immediately followed by degs [mins [secs]]\n");
    prt("Like E13 50 or S15 30 22.456\n");
}

parse_args(@ARGV);


print "Found $g_argcnt arguments ... [$g_args]\n";
if ($g_argcnt < 2) {
    give_help();
    prt("Insufficient arguments\n");
    exit(1);
}
if (length($quad)==0) {
    if ($g_args =~ /(N|E)/i) {
        # nothing
        $quad = $1;
        print "Quadrant: $quad ...\n";
    } elsif ($g_args =~ /(S|W)/i) {
        $degs *= -1;
        $quad = $1;
        print "Quadrant: $quad ...(negative)\n";
    } else {
        print "No quadrant NSEW found ... assumed positive ...[$g_args]\n";
    }
}

print "Degrees = $degs\n";

# now to convert is back
if ($add_reverse_calc) {
    my $nquad = 'N|E';
    if ($degs < 0) {
        $nquad = 'S|W';
        $degs *= -1;
    }
    my $idegs = int($degs);
    my $rmins = ($degs - $idegs) * 60;
    my $imins = int($rmins);
    my $rsecs = ($rmins - $imins) * 60;

    if ($g_argcnt == 1) {
        print "dms = [$g_degs $nquad] ... from [$g_args]\n";
    } elsif ($g_argcnt == 2) {
        print "dms = [$idegs $rmins $nquad] ... from [$g_args]\n";
    } elsif ($g_argcnt == 3) {
        print "dms = [$idegs $imins $rsecs $nquad] ... from [$g_args]\n";
    } else {
        print "dms = [$idegs $imins $rsecs $nquad] ... from [$g_args]\n";
    }

    if (($quad eq 'N')||($quad eq 'S')) {
        print "Latitude: dms=".lat2str($degs)." dm=".lat2strdm($degs)."\n";
    } elsif (($arg eq 'E')||($arg eq 'W')) {
        print "Longitude: dms=".lon2str($degs)." dm:".lon2strdm($degs)."\n";
    }
}
exit(0);

sub next_arg {
    my ($arg,@av) = @_;
    return 0 if (! @av);
    return 1;
}

sub parse_args {
    my @av = @_;
    my $cnt = 0;
    $g_argcnt = scalar @av;
    $g_args = join(" ",@av);
    while (@av) {
        $arg = $av[0];
        if (($arg eq '-?')||($arg eq '-h')||($arg eq '--help')) {
            give_help();
            exit(1);
        }
        if ($cnt == 0) {
            if ($arg =~ /^\d/) {
                # start with a number
            } else {
                $arg = substr($arg,1) if ($arg =~ /^\w/);
            }
            $arg = substr($arg,0,length($arg)-1) if ($arg =~ /\D$/);
            $deg1 = $arg;
            $degs += $arg;
            print "Arg 1 Degrees = $arg ($deg1) = $degs\n";
            $g_degs = $arg;
            if (next_arg(@av)) {
                $arg = $av[1];
                if ($arg =~ /^\d/) {
                    # starts with digit - leave it
                } else {
                    shift @av;
                }
            }
        } elsif ($cnt == 1) {
            $arg = substr($arg,0,length($arg)-1) if ($arg =~ /\D$/);
            $arg2 = $arg;
            $arg2 =~ s/\'//;
            $degs += ($arg2 / 60);
            $deg1 = ($arg2 / 60);
            print "Arg 2 Minutes = $arg ($deg1) = $degs\n";
            if (next_arg(@av)) {
                $arg = $av[1];
                if ($arg =~ /^\d/) {
                    # starts with digit - leave it
                } else {
                    shift @av;
                }
            }
        } elsif ($cnt == 2) {
            $arg2 = $arg;
            $arg2 =~ s/\"//;
            $arg2 = substr($arg2,0,length($arg2)-1) while ($arg2 =~ /\D$/);
            $arg2 = substr($arg2,0,length($arg2)-1) while ($arg2 =~ /\s$/);
            $degs += ($arg2 / (60*60));
            $deg1 = ($arg2 / (60*60));
            print "Arg 3 Seconds = $arg ($deg1) = $degs\n";
            #if (next_arg(@av)) {
            #    $arg = $av[1];
            #    if ($arg =~ /^\d/) {
            #        # starts with digit - leave it
            #    } else {
            #        shift @av;
            #    }
            #}
        } else {
            if (($arg eq 'N')||($arg eq 'S')||($arg eq 'E')||($arg eq 'W')) {
                $quad = $arg;
                $arg2 = int($degs);
                if ($quad =~ /(S|W)/) {
                    print "Quadrant: $quad ...(negative) ";
                    $degs *= -1;
                } else {
                    print "Quadrant: $quad ... ";
                }
                print "$degs ($quad$arg2)\n";
            } else {
                ###$deg1 = 0;
                print "Arg $g_argcnt: Unknown argument [$arg]...\n";
                #exit(1);
            }
        }
        shift @av;
        $cnt++;
    }
}


# eof = dms2ll.pl
