Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW
 
PerlMonks  

Regex strings, deg:mm:ss, and all that

by mcoblentz (Scribe)
on Mar 23, 2008 at 00:15 UTC ( [id://675702]=perlquestion: print w/replies, xml ) Need Help??

mcoblentz has asked for the wisdom of the Perl Monks concerning the following question:

As Holy Week comes to a crescendo, I'm making some small progress on the ol' Cruise Ship project. The head monk is little lazy however, and while this part of the script works ok, I'm wondering if there is a way to tighten this up a bit. It seems, well, simple (Which is not a bad thing).

I could not find any preexisting modules that work with deg:mm:ss the way I needed so building this wasn't too bad - the extra characters in the dataset threw me off a few times though. Is there a better way to put this regex together? Is there a lat/log module I could use? And the input is one field from an array; I separate position into latitude and longitude - how would I return those to an array field? That sounded like a bad idea, so I did not try it.

Anyway, the routine:

sub latlong_cleanup # position comes in; has two parts, lat & long, separated by a comma w +ith trailing stuff, degree symbols, etc. { chomp ($_[0]); #position has trailing cr/lf $_[0] =~ s/\ W\ /\-/; # West longitudes are negative $_[0] =~ s/\ E\ /\ /; # East longitudes are positive $_[0] =~ s/N//; # North latitudes are positive $_[0] =~ s/S\ /\-/; # South latitudes are negative $_[0] =~ s/\'//g; # both lat and longs have trailing strophe. + remove it. $_[0] =~ s/\xB0/\./g; # replace degree character with decimal poin +t (ASCII &#176) # converts lat/long from deg:mm:ss to decimals my $minutes; my ($lat, $long) = split(/,/, $_[0]); # split the position into la +t/long $minutes = ($lat - int($lat)) /0.60; # "decimal-ize" the minutes $minutes = sprintf("%.2f", $minutes); # only need 2 digits; displa +ys are not that good $lat = int($lat) + $minutes; # put it back together $minutes = ($long - int($long) /0.60); # "decimal-ize" the minutes $minutes = sprintf("%.2f", $minutes); # only need 2 digits; displa +ys are not that good $long = int($long) + $minutes; # put it back together #join the lat/long back to $position; it came in as one field in an +array, it probably # should go back that way $_[0] = join(',', $lat,$long); };

Update: I'd like to thank everyone for their suggestions. The first draft of the Cruise Ship project is off and running. You can see a screen shot of the ships in the Caribbean (at night) at my blog.

Replies are listed 'Best First'.
Re: Regex strings, deg:mm:ss, and all that
by hipowls (Curate) on Mar 23, 2008 at 01:30 UTC

    I don't know if you realize it but you are modifying the input string so that

    my $pos = latlong_cleanup($data); # now $pos eq $data

    You can grab the data you want using a single regex. I don't know what format the data is but I'll assume
    23°34'N 12°25'W

    use strict; use warnings; use YAML; use utf8; # to cope with the degree symbol in my test data. my $data = "23°34'N 12°25'W"; print Dump latlong_cleanup($data); sub latlong_cleanup { my $input = shift; if ( my ( $degN, $minN, $NS, $degE, $minE, $EW ) = $input =~ /(\d+)\xB0(\d+)'([NS]) +(\d+)\xB0(\d+)'([EW])/ ) { my $lat = sprintf "%0.2f", ( $NS eq 'S' ? -1 : 1 ) * ( $degN + ( $minN / 60 ) ); my $long = sprintf "%0.2f", ( $EW eq 'W' ? -1 : 1 ) * ( $degE + ( $minE / 60 ) ); return [ $lat, $long ]; } die "Invalid data [$input]"; } __END__ --- - 23.57 - -12.42

Re: Regex strings, deg:mm:ss, and all that
by pc88mxer (Vicar) on Mar 23, 2008 at 02:12 UTC
    If you want to convert N 44°42', E 013°24' to 44.70,13.40, you can do much of editing and extraction of data with just one regular expression:
    my $re = qr/([NS]?)\s*(\d+)(?:\D*)(\d*).*?,\s*([EW]?)\s*(\d+)(?:\D*)(\ +d*)/; unless ($latlong =~ /$re/) { die "unable to parse position\n"; } my $lat = $2 + $3/60; my $long = $5 + $6/60; if ($1 eq 'S') { $lat = -$lat; } if ($4 eq 'W') { $long = -$long; } return sprintf("%.2f,%.2f", $lat, $long);
    The regular expression makes heavy use of greedy and non-greedy matches, so it may be a little difficult to verify that it works, but I'm pretty confident that it's accurate.
Re: Regex strings, deg:mm:ss, and all that
by bobf (Monsignor) on Mar 23, 2008 at 03:22 UTC

    You've already got some ideas for parsing the input. For the conversion step, you could reach for CPAN and Geo::Coordinates::DecimalDegrees.

    use strict; use warnings; use Geo::Coordinates::DecimalDegrees; my ( $degrees, $minutes, $seconds ) = ( 23, 34, 6 ); my $decimal_degrees = dms2decimal( $degrees, $minutes, $seconds ); printf "%d:%d:%d => %.5f\n", $degrees, $minutes, $seconds, $decimal_de +grees; ( $degrees, $minutes, $seconds ) = decimal2dms( $decimal_degrees ); printf "%.5f => %d:%d:%d\n", $decimal_degrees, $degrees, $minutes, $se +conds;
    Output:
    23:34:6 => 23.56833 23.56833 => 23:34:5

    Note that if precision is important you might want to peek at the code and run some tests. For the example above, a round trip conversion from DDMMSS => decimal degrees => DDMMSS results in a one second difference.

    Update: Whups. The printf pattern isn't quite right. Many thanks to BrowserUk++ for catching the error and for providing a much better pattern: %d:%02d:%02.f

      Note that if precision is important you might want to peek at the code and run some tests. For the example above, a round trip conversion from DDMMSS => decimal degrees => DDMMSS results in a one second difference.

      The problem is with your use of sprintf, or rather %d.

      use strict; use warnings; use Geo::Coordinates::DecimalDegrees; my ( $degrees, $minutes, $seconds ) = ( 23, 34, 6 ); my $decimal_degrees = dms2decimal( $degrees, $minutes, $seconds ); printf "%d:%02d:%02.f => %.5f\n", $degrees, $minutes, $seconds, $decimal_degrees; ( $degrees, $minutes, $seconds ) = decimal2dms( $decimal_degrees ); printf "%.5f => %d:%02d:%02.f\n", $decimal_degrees, $degrees, $minutes, $seconds; __END__ c:\test>junk5 23:34:06 => 23.56833 23.56833 => 23:34:06

      The module doesn't int the seconds, allowing for input and output of fractional seconds. If you don't want the fractions, you should use %.f rather than %d. And while your at it, it's conventional to include leading zeros on coordinates, hence %d:%02d:%02.f.


      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://675702]
Approved by lidden
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others drinking their drinks and smoking their pipes about the Monastery: (4)
As of 2024-04-19 03:48 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found