#!/usr/bin/env perl
use warnings;

#
# Written by Paul Lutt, KE7XT & Curt Mills, WE7U.
# Released to the public domain.
#
#
#
# Finds the different lat/long representations corresponding to the
# input numbers.  A space is required between the degrees portion
# and the rest of the input.  Writes out a log file containing APRS
# objects suitable for importing into Xastir, to graphically plot
# the locations of the objects.  Now that Xastir has a server port
# we could directly inject them into the program via that route as
# well, but we currently don't do that.
#
# Converts between different lat/lon formats.  Will also give UMS
# position if the lat/lon resides somewhere inside the Seattle area
# aeronautical map.
#
# UMS coordinates have been used in the past by King County, WA SAR.
# It can be useful for plotting positions on Green Trails maps and
# perhaps other maps.  The maps must be 15' topo maps and marked in
# tenths of miles along the edge in order to make use of this
# coordinate system.
#
# Web pages which discuss UMS format:
#   http://www.impulse.net/~mlynch/land_nav.html
#   http://www.logicsouth.com/~lcoble/dir9/land_nav.htm
#   http://www.aasar.org/training/academy/navigation.pdf
#


use lib "/usr/local/lib";
use Coordinate;         # WE7U's Coordinate.pm module


# Get the username
$user = getlogin;
chomp $user;
$filename = "/var/tmp/PERMUTATIONS-$user.log";


sub convert {

    # Snag the input
    $_ = $_[0];

    print "\n";

    # If the first item has 2 digits and one character and there are
    # three "words" in the input, we're starting with a UTM value.
    if (/^\d\d[a-zA-Z]\s+\w+\s+\w+\s*$/) {

        # printf("Found a UTM value\n");

        # We'll convert it to the standard format first and then run
        # through the rest of the code.

        $zone = $_;
        $easting = $_;
        $northing = $_;

        $zone =~ s/^(\d\d[a-zA-Z])\s+\w+\s+\w+\s*$/$1/;
        $easting =~ s/^\d\d[a-zA-Z]\s+(\w+)\s+\w+\s*$/$1/;
        $northing =~ s/^\d\d[a-zA-Z]\s+\w+\s+(\w+)\s*$/$1/;

        if ($easting > 999999) {
            printf("Easting value is too high!\n");
            return;
        }

        $position->zone($zone);
        $position->easting($easting);
        $position->northing($northing);

        # Convert to lat/lon values
        $position->utm_to_lat_lon();

        # printf("Calculated Lat, Long position(Lat, Long):  %f   %f\n",
        #    $position->latitude(),
        #    $position->longitude() );

        $latitude = $position->latitude();
        $longitude = $position->longitude();

        $lat_dir = "N";
        $long_dir = "E";
 
        if ($latitude < 0.0) {
            $latitude = abs($latitude);
            $lat_dir = "S"
        }
        if ($longitude < 0.0) {
            $longitude = abs($longitude);
            $long_dir = "W";
        }

 
        # printf("%f%s %f%s\n", $latitude,$lat_dir,$longitude,$long_dir);

       $_ = sprintf("%f%s %f%s",
            $latitude,$lat_dir,$longitude,$long_dir);

    }

    # Look for lat/long value in the input

    # Check for N/S/E/W characters in the input.  Set the
    # appropriate flags if found.
    $lat_dir = "N";
    $long_dir = "E";
    if (/S/ || /s/) { $lat_dir  = "S"; }
    if (/W/ || /w/) { $long_dir = "W"; }
    # Filter out these characters from the input
    tr/nsewNSEW//d;

    # Convert to DD MM SS format
    ($lat_deg, $lat_min, $lat_sec,
    $long_deg, $long_min, $long_sec) = split(' ');

    # Decimal Degrees?
    if ($lat_deg =~ /\./) {

        $long_deg = $lat_min;   # Save long_degrees in proper place

        $temp = $lat_deg;
        $lat_deg = int $temp;
        $lat_min = int ((abs($temp) * 60.0) % 60);

        # Modulus converts to integers, so we bump up by 10 and then
        # back down.
        $lat_sec = (abs($temp) * 36000.0) % 600;
        $lat_sec = $lat_sec / 10;

        $temp = $long_deg;

        $long_deg = int $temp;
        $long_min = int ((abs($temp) * 60.0) % 60);
        $long_sec = (abs($temp) * 36000.0) % 600;
        $long_sec = $long_sec / 10;
    }
    # Decimal Minutes?
    elsif ($lat_min =~ /\./) {

        $long_min = $long_deg;  # Save long_minutes in proper place
        $long_deg = $lat_sec;   # Save long_degrees in proper place

        $temp = $lat_min;
        $lat_min = int abs($temp);
        $lat_sec = (abs($temp) * 60.0) % 60;
        $lat_sec = $lat_sec / 10;

        $temp = $long_min;
        $long_min = int abs($temp);
        $long_sec = (abs($temp) * 600.0) % 600;
        $long_sec = $long_sec / 10;
    }
    # Decimal Seconds
    else {  # Already in DD MM SS format, don't convert
    }

    # Print out the three lat/long formats
    printf("                Decimal Degrees: %8.5f%s   %8.5f%s\n",
        $lat_deg + ($lat_min/60.0) + ($lat_sec/3600.0), $lat_dir,
        $long_deg + ($long_min/60.0) + ($long_sec/3600.0), $long_dir );
 
    printf("        Degrees/Decimal Minutes: %02d %06.3f%s  %02d %06.3f%s\n",
        $lat_deg, $lat_min + ($lat_sec/60.0), $lat_dir,
        $long_deg, $long_min + ($long_sec/60.0), $long_dir );

    printf("   Degrees/Minutes/Dec. Seconds: %02d %02d %4.1f%s %02d %02d %4.1f%s\n",
        $lat_deg, $lat_min, $lat_sec, $lat_dir,
        $long_deg, $long_min, $long_sec, $long_dir);

    # Dump out the coordinate in APRS Item format:
    printf(FH "TEST>APRS:)%s!%02d%05.2f%s/%03d%05.2f%s/\n",
        $_[1],
        $lat_deg,
        $lat_min + ($lat_sec/60.0),
        $lat_dir,
        $long_deg,
        $long_min + ($long_sec/60.0),
        $long_dir );

    # Fill in the coordinate object with the current lat/lon.
    # Assuming WGS84 datum
    if ($lat_dir =~ /S/) {
        $position->latitude( -( $lat_deg + ($lat_min/60.0) + ($lat_sec/3600.0) ) );
    }
    else {
        $position->latitude( $lat_deg + ($lat_min/60.0) + ($lat_sec/3600.0) );
    }
 
    if ($long_dir =~ /W/) {
        $position->longitude( -( $long_deg + ($long_min/60.0) + ($long_sec/3600.0) ) );
    }
    else {
        $position->longitude( $long_deg + ($long_min/60.0) + ($long_sec/3600.0) );
    }

    #printf("%f %f\n",$position->latitude,$position->longitude);

    $position->lat_lon_to_utm();
    printf("  Universal Transverse Mercator: %s  %07.0f  %07.0f\n",
        $position->zone(),
        $position->easting(),
        $position->northing() );

    # Check whether the coordinates are within the SEA aeronautical
    # map area
    $lat_err = 0;
    if ($lat_dir =~ /S/ ||
        $lat_deg < 44 || $lat_deg > 49 ||
	    ($lat_deg == 44 && ($lat_min < 30 || ($lat_min == 30 && $lat_sec == 0))) ||
	    ($lat_deg == 49 && ($lat_min > 0 || $lat_sec > 0))) {
	print " lat. out of range ";
	$lat_err = 1;
    }

    $long_err = 0;
    if ($long_dir =~ /E/ ||
        $long_deg < 117 || $long_deg > 125 ||
	    ($long_deg == 117 && ($long_min == 0 && $long_sec == 0)) ||
	    ($long_deg == 125 && ($long_min > 0 || $long_sec > 0))) {
	print " long. out of range";
        $long_err = 1;
    }

    return if ( $lat_err || $long_err);


    # Compute UMS coordinates
    $y_sec = 3600 * ($lat_deg - 44) + 60 * $lat_min + $lat_sec;
    $y_sec = 18000 - $y_sec;

    $x_sec = 3600 * ($long_deg - 117) + 60 * $long_min + $long_sec;
    $x_sec = 28800 - $x_sec;

    $quad = 32 * int($y_sec / 900) + int($x_sec / 900) + 1;

#    print "\tx_sec= $x_sec, y_sec= $y_sec, quad= $quad\n";

    $y_subquad_offset = int($y_sec / 450);
    $x_subquad_offset = int($x_sec / 450);

    if (&even($x_subquad_offset) && &even($y_subquad_offset)) {
        print "        UMS (Green Trails Maps): SEA ${quad} A ";
	printf "%02d", &s2m_x($x_sec - (450 * $x_subquad_offset));
	printf "%02d\n", &s2m_y($y_sec - (450 * $y_subquad_offset));
    } elsif (&odd($x_subquad_offset) && &even($y_subquad_offset)) {
        print "        UMS (Green Trails Maps): SEA ${quad} B ";
	printf "%02d", &s2m_x(450 * ($x_subquad_offset + 1) - $x_sec);
	printf "%02d\n", &s2m_y($y_sec - (450 * $y_subquad_offset));
    } elsif (&even($x_subquad_offset) && &odd($y_subquad_offset)) {
        print "        UMS (Green Trails Maps): SEA ${quad} C ";
	printf "%02d", &s2m_x($x_sec - (450 * $x_subquad_offset));
	printf "%02d\n", &s2m_y(450 * ($y_subquad_offset + 1) - $y_sec);
    } else {
        print "        UMS (Green Trails Maps): SEA ${quad} D ";
	printf "%02d", &s2m_x(450 * ($x_subquad_offset + 1) - $x_sec);
	printf "%02d\n", &s2m_y(450 * ($y_subquad_offset + 1) - $y_sec);
    }
}



sub even {
    return (($_[0] & 1) == 0);
}



sub odd {
    return (($_[0] & 1) == 1);
}



sub s2m_y {
    return (int((0.1917966 * $_[0]) + 0.5));
}



sub s2m_x {
    return (int((cos(($lat_deg + ($lat_min / 60.0) + ($lat_sec / 3600.0)) / 57.29578) * (0.1917966 * $_[0])) + 0.5));
}



##############
# Main Program
##############

open (FH, ">$filename") || die "Couldn't open file for writing:$!\n";


# Create new Coordinate object
$position = Coordinate->new();


$position->datum("WGS 84");    # Datum


print "\n";
print "Examples:  48  07228N   122 07228W\n";
print "           48  08N      122 07W\n";
print "          10U  0565264  5330343\n";

print "\nAPRS Items will be written to: $filename\n";
print "Enter a Lat/Long value or UTM value:\n";


# Snag the input
$input = <>;

# Get rid of whitespace at the beginning
$input =~ s/\s*//;

# UTM value?
if ($input =~ /^\d\d[a-zA-Z]\s+\w+\s+\w+\s*$/) {

    $input2 = $input;
    &convert($input2, "UTM");

print "\n";

    # Swap Easting/Northing values and convert again.  Leave the
    # zone in it's original spot.
    $input2 = $input;
    $input2 =~ s/^(\d\d[a-zA-Z])\s+(\w+)\s+(\w+)\s*$/$1 $3 $2\n/;

print $input2;
    &convert($input2, "UTM2");
}
# else must be lat/lon value
else {

    # Need to break up the input into several possible formats,
    # possibly including swapping lat/long pieces and plotting N/S
    # and E/W variants.

    # I'm going to assume that the user knows his/her approximate
    # lat/lon, so they can input it in roughly the proper format to
    # begin with.  All that's left then is to determine which of the
    # three lat/lon formats it's in.

    # 48  07228N   122 07228W

    $input =~ s/^(\w+)\s+(\d)(\w)\s+(\w+)\s+(\d)(\w)\s*$/$1 0$2$3 $4 0$5$6\n/;

    # DD.DDD
    $input2 = $input;
    $input2 =~ s/^(\w+)\s+(\w+)\s+(\w+)\s+(\w+)\s*$/$1.$2 $3.$4\n/;
print $input2;
    &convert($input2, "DD.DDD");

print "\n";

    # DD MM.MMM
    $input2 = $input;
    $input2 =~ s/^(\w+)\s+(\d\d)(\w+)\s+(\w+)\s+(\d\d)(\w+)\s*$/$1 $2.$3 $4 $5.$6\n/;
    $input2 =~ s/(\.N)/.00N/;
    $input2 =~ s/(\.S)/.00S/;
    $input2 =~ s/(\.E)/.00E/;
    $input2 =~ s/(\.W)/.00W/;
print $input2;
    &convert($input2, "DD MM.MM");

print "\n";

    # DD MM SS.S
    $input2 = $input;
    $input2 =~ s/^(\w+)\s+(\d\d)(\w)\s+(\w+)\s+(\d\d)(\w)\s*$/$1 $2 00.$3 $4 $5 00.$6\n/;
    $input2 =~ s/^(\w+)\s+(\d\d)(\d)(\w)\s+(\w+)\s+(\d\d)(\d)(\w)\s*$/$1 $2 $3.$4 $5 $6 $7.$8\n/;
    $input2 =~ s/^(\w+)\s+(\d\d)(\d\d)(\w+)\s+(\w+)\s+(\d\d)(\d\d)(\w+)\s*$/$1 $2 $3.$4 $5 $6 $7.$8\n/;
    $input2 =~ s/(\.N)/.0N/;
    $input2 =~ s/(\.S)/.0S/;
    $input2 =~ s/(\.E)/.0E/;
    $input2 =~ s/(\.W)/.0W/;
    $input2 =~ s/\s(\d)(\.\d)/ 0$1$2/g;
print $input2;
    &convert($input2, "DD MM SS");

}

close(FH);
