Velocity Reviews > Perl > Varying a Latitude/Longitude

# Varying a Latitude/Longitude

essary@gmail.com
Guest
Posts: n/a

 11-02-2005
A while ago while I was in the Army I co-wrote an exercise SIGINT
generator with a friend. We made a mess of a few functions called
VaryLat and VaryLong. The functions basically took in two arguments: a
Latitude or Longitude and an amount to vary it by(in seconds). The
second parameter could be any number positive or negative. The real
issues started rising when we figured out that crossing over either the
90' mark or the 180' mark was throwing off our numbers. The functions
turned out to be very long seeing as how neither of us was very
experience with Perl at the time. I wish I had the code to post to show
how messy this thing was but seeing as how it was on a Top Secret//SCI
machine security protocols made if impossible to bring a copy of this
source home at the time. Basically I was wondering if anyone else has
had this particular problem and came up with a good solution. It's been
bugging me ever since I wrote the stupid code. The function prototype
was something like this: VaryLat(\$theLatitude,\$varyAmount).

usenet@DavidFilmer.com
Guest
Posts: n/a

 11-02-2005
http://www.velocityreviews.com/forums/(E-Mail Removed) wrote:
> Basically I was wondering if anyone else has
> had this particular problem and came up with a good solution.

There are a whole bunch of modules on CPAN that deal with latitude and
longitude. I'll bet one of them can do these calculations, but nothing
jumped out at me when I very quickly skimmed over the one-liner
descriptions.

essary@gmail.com
Guest
Posts: n/a

 11-03-2005
Yeah, we did it horribly I think. Here's the code we had:

sub varyLat{

\$lat = \$_[0];

\$latVariant = \$locVariantSec - floor(rand(\$locVariantSec * 2));

\$degrees = substr(\$lat, 0, 2);
\$minutes = substr(\$lat, 2, 2);
\$seconds = substr(\$lat, 4, 2);
\$ns = substr(\$lat, 6, 1);
\$delta = 1;
if (\$latVariant < 0){
\$delta = -1;
\$latVariant = \$latVariant * -1;
}
\$degVariant = floor(\$latVariant / 3600) * \$delta;
if (\$degVariant > 0){
\$latVariant = \$latVariant - (\$degVariant * 3600);
}
\$minVariant = floor(\$latVariant / 60) * \$delta;
if (\$minVariant > 0){
\$latVariant = \$latVariant - (\$minVariant * 60);
}
\$secVariant = (\$latVariant % 60) * \$delta;
fixSeconds;
fixMinutes;
fixLatDegrees;
\$degrees = &fixLength(\$degrees,2,0);
\$minutes = &fixLength(\$minutes,2,0);
\$seconds = &fixLength(\$seconds,2,0);

return \$degrees . \$minutes . \$seconds. \$ns;
}

Here's the code for fixSeconds and fixMinutes and fixLatDegrees:

sub fixSeconds{

\$seconds = \$seconds + \$secVariant;

if (\$seconds < 0){
\$minVariant--;
\$seconds = 60 + \$seconds;
}
else{
if (\$seconds > 59){
\$minVariant++;
\$seconds = \$seconds % 60;
}
}
}

sub fixMinutes{

\$minutes = \$minutes + \$minVariant;

if (\$minutes < 0){
\$degVariant--;
\$minutes = 60 + \$minutes;
}
else{
if (\$minutes > 59){
\$degVariant++;
\$minutes = \$minutes % 60;
}
}
}

sub fixLatDegrees{

\$degrees = \$degrees + \$degVariant;

CASE:{

((\$degrees eq 90) and (\$minutes eq 0) and (\$seconds eq 0)) && do{

last CASE;
};

((\$degrees < 90) and (\$degrees >= 0)) && do{

last CASE;
};

(\$degrees >= 90) && do{
\$degrees = 90 - (\$degrees % 90) - 1;
\$minutes = 60 - (\$minutes % 60) - 1;
\$seconds = 60 - (\$seconds % 60);
last CASE;
};

(\$degrees < 0) && do{
\$degrees = 90 - (\$degrees % 90) - 1;
\$minutes = 60 - (\$minutes % 60) - 1;
\$seconds = 60 - (\$seconds % 60);
if (\$ns eq 'S'){
\$ns = 'N';
}
else{
\$ns = 'S';
}
last CASE;
};
}
}

jl_post@hotmail.com
Guest
Posts: n/a

 11-03-2005
(E-Mail Removed) wrote:
>
> \$degrees = substr(\$lat, 0, 2);
> \$minutes = substr(\$lat, 2, 2);
> \$seconds = substr(\$lat, 4, 2);

What happens if \$lat is a string like "-214530"? Then \$degrees will
be "-2", \$minutes will be "14", and \$seconds will be "53". I don't
think that's what you intend to happen.

You might want to try something like this:

return unless \$lat =~ m/^(-?\d+)(\d{2})(\d{2})\$/;
my (\$degrees, \$minutes, \$seconds) = (\$1, \$2, \$3);

That way, degrees will be set to "-21", \$minutes will be set to "45",
and \$seconds will be set to "30", which is probably what you wanted.
If \$lat happens to be malformed, then the function returns without
modifying anything.

I notice that you are using a lot of global variables. Many of
programmers (me included) frown at that habit, and recommend using only
variables passed into functions (by way of their parameter lists) and
only modifying variables declared inside those functions. Should any
variables need to be changed that exist outside the function, then
their values should be set at the function call, like this:

\$lat = varyLat(\$lat, \$latVariant);

That way it'll be much, much easier to keep track of what each function
modifies and uses to compute its return values.

And I truly recommend that you use the lines:

use strict;
use warnings;

at the top of all your Perl programs. They catch SO MANY errors!
(Even very subtle ones!) Some people are discouraged from using
'strict' because they can't get their program to work with it, but what
they really should be doing is asking for help from a more experienced
programmer on how to get their program to run with 'strict' (instead of
removing it).

So consider using 'strict' and 'warnings', as well as not using
much easier to debug and maintain.

Hope this helps,

-- Jean-Luc

samwyse@gmail.com
Guest
Posts: n/a

 11-03-2005

(E-Mail Removed) wrote:
> (E-Mail Removed) wrote:
> >
> > \$degrees = substr(\$lat, 0, 2);
> > \$minutes = substr(\$lat, 2, 2);
> > \$seconds = substr(\$lat, 4, 2);

>
> What happens if \$lat is a string like "-214530"? Then \$degrees will
> be "-2", \$minutes will be "14", and \$seconds will be "53". I don't
> think that's what you intend to happen.
>
> You might want to try something like this:
>
> return unless \$lat =~ m/^(-?\d+)(\d{2})(\d{2})\$/;
> my (\$degrees, \$minutes, \$seconds) = (\$1, \$2, \$3);
>
> That way, degrees will be set to "-21", \$minutes will be set to "45",
> and \$seconds will be set to "30", which is probably what you wanted.
> If \$lat happens to be malformed, then the function returns without
> modifying anything.

You'll notice that a lot of the time-related functions do things in
terms of seconds and only convert back to more conventionally formats
at the very end. So, I'd suggest using this for the second line:
my \$lat_sec = (60 * \$1 + \$2) * 60 + \$3);

If you really want to use degrees, then use this:
my \$lat_deg = \$1 + (\$2 + \$3 / 60) / 60;
But this can be prone to rounding errors, since 3600 isn't a power of
two.

jl_post@hotmail.com
Guest
Posts: n/a

 11-03-2005
> (E-Mail Removed) wrote:
> >
> > You might want to try something like this:
> >
> > return unless \$lat =~ m/^(-?\d+)(\d{2})(\d{2})\$/;
> > my (\$degrees, \$minutes, \$seconds) = (\$1, \$2, \$3);

(E-Mail Removed) replied:
>
> You'll notice that a lot of the time-related functions do things in
> terms of seconds and only convert back to more conventionally formats
> at the very end. So, I'd suggest using this for the second line:
> my \$lat_sec = (60 * \$1 + \$2) * 60 + \$3);

That's a good point. But just be careful about the fact that if \$1
is a negative value, then \$2 and \$3 should be subtracted instead of
added. These lines should take care of that:

return unless \$lat =~ m/^(-?)(\d+)(\d{2})(\d{2})\$/;
my \$lat_sec = (60 * \$2 + \$3) * 60 + \$4);
\$lat_sec *= -1 if defined \$1 and \$1 eq '-';