``````package Geo::Coordinates::VandH;
use strict;
use Math::Trig;
use Math::Complex; #fixes a few one-off problems with negative square roots
use vars qw(\$VERSION);

\$VERSION = '1.11';

sub new {
my \$package = shift;
return bless({}, \$package);
};

sub distance {
my \$self = shift;
my \$v1;
my \$h1;
my \$v2;
my \$h2;
my \$miles;
my \$vd;
my \$hd;
my \$sum;
my \$count;
(\$v1,\$h1,\$v2,\$h2) = @_;
\$vd=\$v1-\$v2;
if (\$vd < 0)  { \$vd=\$vd*-1; };
\$hd=\$h1-\$h2;
if (\$hd < 0)  { \$hd=\$hd*-1; };
\$vd=\$vd/3;
\$hd=\$hd/3;
\$count=1;
while ((\$vd**2+\$hd**2) > 1777) {
\$vd=\$vd/3;
\$hd=\$hd/3;
\$count++;
};
\$sum=\$vd**2+\$hd**2;
\$sum = \$sum * ( 0.1 * (9**\$count));
\$miles=sqrt(\$sum);
#round up to next greatest mile
if ((\$miles-int(\$miles)) > 0) { \$miles=int(\$miles)+1; };
return \$miles;
};

sub vh2ll {
my \$self = shift;
#Constants shamelessly stolen from vh2ll.c
my \$m_pi=3.14159265358979323846;
my \$transv=6363.235;
my \$transh=2250.7;
my \$rotc=0.23179040;
my \$rots=0.97276575;
my \$ex=0.40426992;
my \$ey=0.68210848;
my \$ez=0.60933887;
my \$wx=0.65517646;
my \$wy=0.37733790;
my \$wz=0.65449210;
my \$px=-0.555977821730048699;
my \$py=-0.345728488161089920;
my \$pz=0.755883902605524030;
my \$gx=0.216507961908834992;
my \$gy=-0.134633014879368199;
my \$a=0.151646645621077297;
my \$q=-0.294355056616412800;
my \$q2=0.0866448993556515751;
my @bi = ( 1.00567724920722457, -0.00344230425560210245, 0.000713971534527667990, -0.0000777240053499279217, 0.00000673180367053244284, -0.000000742595338885741395,  0.0000000905058919926194134 );
my \$x;
my \$y;
my \$z;
my \$v;
my \$h;
my \$delta;
(\$v,\$h) = @_;
my \$t1 = (\$v - \$transv) / \$radius;
my \$t2 = (\$h - \$transh) / \$radius;
my \$vhat = \$rotc*\$t2 - \$rots*\$t1;
my \$hhat = \$rots*\$t2 + \$rotc*\$t1;
my \$e = cos(sqrt(\$vhat*\$vhat + \$hhat*\$hhat));
my \$w = cos(sqrt(\$vhat*\$vhat + (\$hhat-0.4)*(\$hhat-0.4)));
my \$fx = \$ey*\$w - \$wy*\$e;
my \$fy = \$ex*\$w - \$wx*\$e;
my \$b = \$fx*\$gx + \$fy*\$gy;
my \$c = \$fx*\$fx + \$fy*\$fy - \$q2;
my \$disc = \$b*\$b - \$a*\$c; #discriminant
if (\$disc == 0.0) { #It's right on the E-W axis
\$z = \$b/\$a;
\$x = (\$gx*\$z - \$fx)/\$q;
\$y = (\$fy - \$gy*\$z)/\$q;
} else {
\$delta = sqrt(\$disc);
\$z = (\$b + \$delta)/\$a;
\$x = (\$gx*\$z - \$fx)/\$q;
\$y = (\$fy - \$gy*\$z)/\$q;
if ( \$vhat * ( \$px*\$x + \$py*\$y + \$pz*\$z) < 0 ) { #wrong direction
\$z = (\$b - \$delta)/\$a;
\$x = (\$gx*\$z - \$fx)/\$q;
\$y = (\$fy - \$gy*\$z)/\$q;
};
};
my \$lat=asin(\$z);
#Use polynomial approximation for inverse mapping
#(sphere to spheroid)
my \$lat2 = \$lat*\$lat;
my \$earthlat = 0;
for (my \$i=6; \$i>=0 ; \$i--) {
\$earthlat = (\$earthlat + \$bi[\$i]) * (\$i? \$lat2 : \$lat);
};
\$earthlat *= 180/\$m_pi;
# Adjust Longitude by 52 degrees
my \$lon = atan2(\$x,\$y) * 180/\$m_pi;
my \$earthlon = \$lon + 52.0000000000000000;
return (\$earthlat,\$earthlon);
};
1;
__END__
# Below is stub documentation for your module. You better edit it!

Geo::Coordinates::VandH - Convert and Manipulate telco V and H coordinates

To convert V: 5498 H: 2895 to lat/long coordinates:

use Geo::Coordinates::VandH;
\$blah=new Geo::Coordinates::VandH;
(\$lat,\$lon) = \$blah->vh2ll(5498,2895);
printf "%lf,%lf\n",\$lat,\$lon;

To find the mileage between 5498,2895 and 5527,2873 in miles:

use Geo::Coordinates::VandH;
\$blah=new Geo::Coordinates::VandH;
printf "Distance between Pontiac, MI and Southfield, MI is approximately: %d miles\n",\$blah->distance(5498,2895,5527,2873);

Currently this package supports the translation of V+H to Lat/Long, and mileage calculations between two V+H coordinates.
Results are returned in decimal degrees for V+H to Lat/Long, and Miles for distance.
Future versions will convert Lat/Long to V+H coordinates.

Paul Timmins, E<lt>paul@timmins.netE<gt>

L<perl>.

=cut

``````