jcoxen has asked for the
wisdom of the Perl Monks concerning the following question:
I need to convert location coordinates from Latitude/Longitude to V&H (Vertical/Horizontal) and back again. For those who've never come across it, V&H is a coordinate system developed by and used heavily in the Telco industry. It's used to calculate mileage for circuits via the formula
sqrt((V1V2)^2+(H1H2)^2/10)
I can use Geo::Coordinates::VandH to convert V&H to Lat/Lon but I've struck out on a package or algorithm to convert Lat/Lon to V&H. I've found simple calculator programs to convert one coordinate pair at a time but I have to convert potentially tens of thousands of coordinate pairs as a cross check on existing records. Doing this one pair at a time isn't an option. Is anyone aware of a solution for this?
Thanks,
Jack
Re: Lat/Lon to V&H conversion by Random_Walk (Parson) on Oct 14, 2004 at 12:47 UTC 
Do you really need to convert back again ? If you are converting them from Lat/Long in the first place why not maintain a hash (or tied db if too many for hash) keyed on V&H of each Lat/Long you convert ? This assumes you are not performing any transformations of the resultant V&H only calculating various mileage distances as given above.
The other possibility is to calculate mileage directly from the Lat/Long pairs, this is not too hard (I can't remember the formula now but look up spherical geometry or celestial navigation (as in with a sextant, not for spacecraft :) You can solve a spherical triangle based on the angle your two points make with one of the poles and the distance between each point and the pole (90Lat)*CircEarth/360(in your desired units).
# why is there not a <pseudocode> tag :) ?
# here is the triangle we solve...
#
# A (North Pole)
# / \
# c b
# / \
# BaC
#
# Point A is the north pole, B an C are all yours
# $pointB $latB, $longB
# $pointC $latC, $longC
# here is the formula
# cos a = cos b * cos c + sin b * sin c + cos A
$miles_per_degree=$Earths_Circumference/360;
$Angle_A=abs($longB  $longC); # needs fix for wrapping round the prim
+e meridian
$Len_b = (90LongC)*$miles_per_degree; # fix for south of equator
$Len_c = (90LongB)*$miles_per_degree; # make degrees S negative ?
# plug those three into the spherical circle equasion
Cheers, R.  [reply] [d/l] 

 [reply] 
Re: Lat/Lon to V&H conversion by Yendor (Pilgrim) on Oct 14, 2004 at 12:56 UTC 
A quick Google search on "Convert V&H to lat long" (sans quotes) turned up this message from the ispclec mailing list. It contains C code that was converted from FORTRAN. Perhaps you (or someone) could convert it to Perl and either update the Geo::Coordinates::VandH modules, or at least email the module author.
 [reply] 

 [reply] 

Well, fine. If you're that lazy...I had a few spare cycles.
NOTE: The following code is completely UNTESTED. I will not guarantee anything about it. I will not even guarantee that it will compile, much less produce the correct output. For all I know, there's some secret obfu in there that converts everything to rm r /. It is a straightup conversion of the code found in the link I referenced earlier. Use at your own risk.
my $y # V, the vertical component of VH
my $x # H, the horizontal component of VH
# FUNCTION NAME: convert_geo_vh()
# INPUTS: double b (latitude)
# double l (longitude)
# OUTPUT: double y (V, the vertical component of VH)
# double x (H, the horizontal component of VH)
# PURPOSE:
# This function converts latitude and longitude (in radians) to
# VH coordinates (unit of measure square root of 0.1 mile).
# This function returns VH coordinates with two decimal places:
# VVVV.VV as the Ycoordinate and HHHH.HH as the Xcoordinate.
# This is precise to only a "16.7 x 16.7"foot square. The
# distortion inherent in a continentwide projection makes any
# more than two decimal places meaningless. Thus, any conversion
# to or from VH coordinates is only an approximate location.
sub convert_geo_vh() {
my $b = shift;
my $l = shift;
my ($i, $v, $h);
my ($h1, $v1, $e, $w, $e1, $w1);
my ($xx, $yy, $zz, $p, $q);
my ($kk, $ll, $k1, $k3, $k5, $k7, $k9);
$k1 = 0.99435487 * $b;
$k3 = 0.00336523 * $b * $b * $b;
$k5 = 0.00065596 * $b * $b * $b * $b * $b;
$k7 = 0.00005606 * $b * $b * $b * $b * $b * $b * $b;
$k9 = 0.00000188 * $b * $b * $b * $b * $b * $b * $b * $b * $b;
$kk = $k1 + $k3  $k5 + $k7  $k9;
$xx = sin($kk);
$ll = $l  0.907571211;
$yy = cos($kk) * sin($ll);
$zz = cos($kk) * cos($ll);
$e1 = (0.60933887 * $xx) + (0.40426992 * $yy) + (0.68210848 * $zz);
if ($e1 > 1.0)
$e1 = 1.0;
$q = sqrt(fabs(1.0  ($e1 * $e1)));
$e = asin($q);
$w1 = (0.6544921 * $xx) + (0.65517646 * $yy) + (0.3773379 * $zz);
if ($w1 > 1.0)
$w1 = 1.0;
$q = sqrt(fabs(1.0  ($w1 * $w1)));
$w = asin($q);
$h1 = (($e * $e)  ($w * $w) + 0.16) / 0.8;
$v1 = sqrt(fabs(($e * $e)  ($h1 * $h1)));
$i = (10.0*($xx  (0.73553336 * $yy)  (0.45738305 * $zz)) + 0.5);
if ($i < 0)
$i;
$p = $i;
$p = $p / 100000000.0;
if ($p < 0)
$v1 *= 1.0;
if ($p == 0)
$v1 = 0.0;
$v = ((6363.235 + (2893.0 * $h1)  (12141.19003 * $v1)) * 100.0 + 0.
+5);
$h = ((2250.7 + (12141.19003 * $h1) + (2893.0 * $v1)) * 100.0 + 0.5)
+;
$y = $v / 100.0;
$x = $h / 100.0;
$c = 0.0;
$k = 1.0;
return;
}
 [reply] [d/l] 
Re: Lat/Lon to V&H conversion by tachyon (Chancellor) on Oct 15, 2004 at 05:10 UTC 
Here is an Inline C version that swings both way. It is a port of the Java code used in openmap VHTransform class, and unlike the other code you linked to actually works properly. http://openmap.bbn.com/doc/api/com/bbn/openmap/util/VHTransform.html
 [reply] [d/l] 

 [reply] 
Re: Lat/Lon to V&H conversion by tachyon (Chancellor) on Oct 15, 2004 at 06:58 UTC 
 [reply] 

 [reply] 

UPDATE  I installed Geo::Coordinates::VandH::XS on my test/development box and put together a quick little test program. After I modified the code to explicitly call Geo::Coordinates::VandH::XS::toVH, the test program worked. Except it gave the wrong answer. The curious thing is, it gave the exact same wrong answer as my convertedoverfromC program.
All this leads me to think that there's a flaw in the original code somewhere since there's no way I could have made a mistake...unless, or course, I did make a mistake. So, just to make sure, here's my converted code...
 [reply] [d/l] [select] 

V = 7349.7333432988 Latitude = 40.423792190582
H = 5909.05477294282 Longitude = 104.791265047498
I was to lazy to grok why it should be so. There are comments about the sign for EW and NS and also notes that it is US centric. I would be interested in some clarification on the sign issue. Evidently one is 'wrong'. Its easy to fix of course.
With the XS code (which will be around 100x faster than pure perl) you need to import the functions you want to avoid a fully qualified call. It is good practice with modules not to export by default.
# import all the available functions specifying by name
use Geo::Coordinates::VandH::XS qw( toVH toLatLon distance degrees rad
+ians );
# or as documented in the very sparse docs ;)
use Geo::Coordinates::VandH::XS qw( :all );
 [reply] [d/l] [select] 

Re: Lat/Lon to V&H conversion by perlcapt (Pilgrim) on Oct 15, 2004 at 21:26 UTC 
I might suggest you make your coordinate transform to UTM then make your distance calculations or next transform. The Geo::Coordinates::UTM module is bidirectional and supports many different Ellipsoids for the Lat/Long. I suggest using the WGS84 ellipsoid since that is the standard for GPS. The precision of these calculations is to the centimeter if not better, assuming that the earth is not a pulsating ellipsoid, which it really is. The one drawback is that the UTM system divides the planet into zones. Calculations across zone bounderys may not be as precise.
We (Center for Coastal and Ocean Mapping) do a lot of this, but in the metric system. We need that flat model to do measurement calculations.  [reply] 

 [reply] 

