Amateur radio operators use the Maidenhead Grid Locator Square system to identify location. For example, the location FN03ir describes a small area in Eastern Toronto, centred on 43.72917°N, 79.29167°W. It's a form of geohash that degrades gracefully in precision.

I've written a two-way converter that seems to work well. Other people have expressed interest in using it. I have a nagging suspicion, though, that the sub maidenhead2latlong could be a lot more elegant, but I've been out of everyday programming for too long to see what's up with it. Any hints, please?

Some test data:

Callsign Locator   Latitude  Longitude
======== ========= ========= ========== 
LU5VV    FE48hu    -41.14583  -71.37500
OX3WS    GP44dd     64.14583  -51.70833
AH6V     BK29jv     19.89583 -155.20833
ZL3TE    RF73ic    -36.89583  174.70833

... and now the code:

#!/usr/bin/perl -w # maidenhead - 6 character locator # created by scruss/VA3PID on 02011/04/01 # RCS/CVS: $Id: maidenhead,v 1.3 2011/04/03 11:04:38 scruss Exp $ use strict; sub latlong2maidenhead; sub maidenhead2latlong; if ( $#ARGV >= 1 ) { # two or more args # convert lat/long to grid print latlong2maidenhead( $ARGV[0], $ARGV[1] ), "\n"; } elsif ( $#ARGV == 0 ) { # one arg # convert grid to lat/long print join( ", ", maidenhead2latlong( $ARGV[0] ) ), "\n"; } else { # no args # print usage print 'Usage: ', $0, ' dd.dddddd ddd.dddddd', "\n", ' or', "\n", $0, ' grid_location', "\n"; } exit; sub maidenhead2latlong { # convert a Maidenhead Grid location (eg FN03ir) # to decimal degrees # this code could be cleaner/shorter/clearer my @locator = split( //, uc(shift) ); # convert arg to upper case array my $lat = 0; my $long = 0; my $latdiv = 0; my $longdiv = 0; my @divisors = ( 72000, 36000, 7200, 3600, 300, 150 ) ; # long,lat field size in seconds my $max = ( $#locator > $#divisors ) ? $#divisors : $#locator; for ( my $i = 0 ; $i <= $max ; $i++ ) { if ( int( $i / 2 ) % 2 ) { # numeric if ( $i % 2 ) { # lat $latdiv = $divisors[$i]; # save for later $lat += $locator[$i] * $latdiv; } else { # long $longdiv = $divisors[$i]; $long += $locator[$i] * $longdiv; } } else { # alpha my $val = ord( $locator[$i] ) - ord('A'); if ( $i % 2 ) { # lat $latdiv = $divisors[$i]; # save for later $lat += $val * $latdiv; } else { # long $longdiv = $divisors[$i]; $long += $val * $longdiv; } } } $lat += ( $latdiv / 2 ); # location of centre of square $long += ( $longdiv / 2 ); return ( ( $lat / 3600 ) - 90, ( $long / 3600 ) - 180 ); } sub latlong2maidenhead { # convert a WGS84 coordinate in decimal degrees # to a Maidenhead grid location my ( $lat, $long ) = @_; my @divisors = ( 72000, 36000, 7200, 3600, 300, 150 ); # field size in seconds my @locator = (); # add false easting and northing, convert to seconds $lat = ( $lat + 90 ) * 3600; $long = ( $long + 180 ) * 3600; for ( my $i = 0 ; $i < 3 ; $i++ ) { foreach ( $long, $lat ) { my $div = shift(@divisors); my $part = int( $_ / $div ); if ( $i == 1 ) { # do the numeric thing for 2nd pair push @locator, $part; } else { # character thing for 1st and 3rd pair push @locator, chr( ( ( $i < 1 ) ? ord('A') : ord('a') ) + $part ); } $_ -= ( $part * $div ); # leaves remainder in $long or $lat } } return join( '', @locator ); }

--
bowling trophy thieves, die!


In reply to Locator to Lat/Long code: works, but is inelegant by Willard B. Trophy

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.