xastir/scripts/coord-convert.pl
Tom Russo cd2be5c7ba Remove "-W" and "-w" from shebang, then "use warnings"
Wheen I replaced the non-portable "#!/usr/bin/perl" with the portable
"#!/usr/bin/env perl" I neglected the fact that some scripts have a -w
or -W argument after the command.  This can't work with env, and the
correct approach is to put "use warnings;" in the script instead.
2019-04-18 17:16:53 -06:00

279 lines
8.2 KiB
Perl
Executable file

#!/usr/bin/env perl
use warnings;
#
# Written by Paul Lutt, KE7XT.
# Released to the public domain.
#
#
#
#
# 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 "${prefix}/lib";
use Coordinate; # WE7U's Coordinate.pm module
# Create new Coordinate object
my $position = Coordinate->new();
$position->datum("WGS 84"); # Datum
print "\n";
print "Examples: 48.123N 122.123W\n";
print " 48 07.380N 122 07.380W\n";
print " 48 07 22.8N 122 07 22.8W\n";
print " 10U 0565264 5330343\n";
while (1) {
print "\nEnter a Lat/Long value or UTM value:\n";
# Snag the input
$_ = <>;
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");
next;
}
$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
# Add missing decimal points.
s/^(\d+)([NSns])\s+(\d+)([EWew])\s*$/$1.$2 $3.$4/;
# 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) * 600.0) % 600;
$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);
# 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;
}
next 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));
}