Geo-HelmertTransform
view release on metacpan or search on metacpan
lib/Geo/HelmertTransform.pm view on Meta::CPAN
=cut
use constant M_PI => 3.141592654;
=item rad_to_deg RADIANS
Convert RADIANS to degrees.
=cut
sub rad_to_deg ($) {
return 180. * $_[0] / M_PI;
}
=item deg_to_rad DEGREES
Convert DEGREES to radians.
=cut
sub deg_to_rad ($) {
return M_PI * $_[0] / 180.;
}
=item geo_to_xyz DATUM LAT LON H
Return the Cartesian (X, Y, Z) coordinates for the geographical coordinates
(LAT, LON, H) in the given DATUM.
=cut
sub geo_to_xyz ($$$$) {
my ($datum, $lat, $lon, $h) = @_;
$lat = deg_to_rad($lat);
$lon = deg_to_rad($lon);
my $v = $datum->a() / sqrt(1 - $datum->e2() * sin($lat) ** 2);
return (
($v + $h) * cos($lat) * cos($lon),
($v + $h) * cos($lat) * sin($lon),
((1 - $datum->e2()) * $v + $h) * sin($lat)
);
}
=item xyz_to_geo DATUM X Y Z
Return the geographical (LAT, LON, H) coordinates for the Cartesian coordinates
(X, Y, Z) in the given DATUM. This is an iterative procedure.
=cut
sub xyz_to_geo ($$$$) {
my ($datum, $x, $y, $z) = @_;
my ($lat, $lat2, $lon, $h, $v, $p);
$lon = atan2($y, $x);
$p = sqrt($x**2 + $y**2);
$lat2 = atan2($z, $p);
my $niter = 0;
do {
$lat = $lat2;
$v = $datum->a() / sqrt(1 - $datum->e2() * sin($lat) ** 2);
$lat2 = atan2(($z + $datum->e2() * $v * sin($lat)), $p);
die "exceeded 10000 iterations without converging in Geo::HelmertTransform::xyz_to_geo"
if (++$niter > 10000);
} while (abs($lat2 - $lat) > 2e-6); # about 1/10000 mile
$h = $p / cos($lat) - $v;
return (rad_to_deg($lat), rad_to_deg($lon), $h);
}
=item convert_datum D1 D2 LAT LON H
Given geographical coordinates (LAT, LON, H) in datum D1, return the
corresponding coordinates in datum D2. This assumes that the transformations
are small, and always converts via WGS84.
=cut
sub convert_datum ($$$$$) {
my ($d1, $d2, $lat, $lon, $h) = @_;
my ($x1, $y1, $z1) = geo_to_xyz($d1, $lat, $lon, $h);
my ($x, $y, $z) = ($x1, $y1, $z1);
if (!$d1->is_wgs84()) {
# Transform into WGS84.
$x = $d1->tx()
+ (1 + $d1->s()) * $x1
- $d1->rz() * $y1
+ $d1->ry() * $z1;
$y = $d1->ty()
+ $d1->rz() * $x1
+ (1 + $d1->s()) * $y1
- $d1->rx() * $z1;
$z = $d1->tz()
- $d1->ry() * $x1
+ $d1->rx() * $y1
+ (1 + $d1->s()) * $z1;
}
my ($x2, $y2, $z2) = ($x, $y, $z);
if (!$d2->is_wgs84()) {
$x2 = -$d2->tx()
+ (1 - $d2->s()) * $x
+ $d2->rz() * $y
- $d2->ry() * $z;
$y2 = -$d2->ty()
- $d2->rz() * $x
+ (1 - $d2->s()) * $y
+ $d2->rx() * $z;
$z2 = -$d2->tz()
+ $d2->ry() * $x
- $d2->rx() * $y
+ (1 - $d2->s()) * $z;
}
return xyz_to_geo($d2, $x2, $y2, $z2);
}
=item datum NAME
Return the datum of the given NAME. Currently implemented are:
=over 4
( run in 0.615 second using v1.01-cache-2.11-cpan-71847e10f99 )