package Geo::Coordinates::ITM; use warnings; use strict; use base qw( Exporter ); our @EXPORT_OK = qw( ll_to_grid grid_to_ll ); use Carp; use Math::Trig; =head1 NAME Geo::Coordinates::ITM - Convert coordinates between lat/lon and Irish Transverse Mercator =head1 VERSION This document describes Geo::Coordinates::ITM version 0.02 =cut our $VERSION = '0.02'; =head1 SYNOPSIS use Geo::Coordinates::ITM qw( ll_to_grid grid_to_ll ); my ( $lat, $lon ) = grid_to_ll( $east, $north ); my ( $east, $north ) = ll_to_grid( $lat, $lon ); =head1 DESCRIPTION Convert back and forth between Irish Transverse Mercator grid and WGS84. The conversion code was stolen wholesale from L. http://svn.geograph.org.uk/svn/branches/british-isles/libs/geograph \ /conversionslatlong.class.php Nothing is exported by default. The exportable functions are C and C. =head1 INTERFACE =head2 C<< ll_to_grid >> Convert a latitude, longitude (WGS84) coordinate pair into an ITM easting and northing. my ( $east, $north ) = ll_to_grid( $lat, $lon ); =cut sub ll_to_grid { my ( $lat, $long ) = @_; return ( _ll2e( $lat, $long, 6378137, 6356752.314, 600000, 0.999820, 53.50000, -8.00000 ), _ll2n( $lat, $long, 6378137, 6356752.314, 600000, 750000, 0.999820, 53.50000, -8.00000 ) ); } =head2 C<< grid_to_ll >> Convert an ITM easting, northing pair to a WGS84 latitude, longitude. my ( $lat, $lon ) = grid_to_ll( $east, $north ); =cut sub grid_to_ll { my ( $e, $n ) = @_; return ( _en2lat( $e, $n, 6378137, 6356752.314, 600000, 750000, 0.999820, 53.50000, -8.00000 ), _en2lon( $e, $n, 6378137, 6356752.314, 600000, 750000, 0.999820, 53.50000, -8.00000 ) ); } sub _ll2e { my ( $PHI, $LAM, $a, $b, $e0, $f0, $PHI0, $LAM0 ) = @_; my $RadPHI = deg2rad( $PHI ); my $RadLAM = deg2rad( $LAM ); my $RadPHI0 = deg2rad( $PHI0 ); my $RadLAM0 = deg2rad( $LAM0 ); my $af0 = $a * $f0; my $bf0 = $b * $f0; my $e2 = ( $af0**2 - $bf0**2 ) / $af0**2; my $n = ( $af0 - $bf0 ) / ( $af0 + $bf0 ); my $nu = $af0 / ( sqrt( 1 - ( $e2 * sin( $RadPHI )**2 ) ) ); my $rho = ( $nu * ( 1 - $e2 ) ) / ( 1 - ( $e2 * sin( $RadPHI )**2 ) ); my $eta2 = ( $nu / $rho ) - 1; my $p = $RadLAM - $RadLAM0; my $IV = $nu * ( cos( $RadPHI ) ); my $V = ( $nu / 6 ) * ( cos( $RadPHI )**3 ) * ( ( $nu / $rho ) - ( tan( $RadPHI )**2 ) ); my $VI = ( $nu / 120 ) * ( cos( $RadPHI )**5 ) * ( 5 - ( 18 * ( tan( $RadPHI )**2 ) ) + ( tan( $RadPHI )**4 ) + ( 14 * $eta2 ) - ( 58 * ( tan( $RadPHI )**2 ) * $eta2 ) ); return $e0 + ( $p * $IV ) + ( $p**3 * $V ) + ( $p**5 * $VI ); } sub _ll2n { my ( $PHI, $LAM, $a, $b, $e0, $n0, $f0, $PHI0, $LAM0 ) = @_; my $RadPHI = deg2rad( $PHI ); my $RadLAM = deg2rad( $LAM ); my $RadPHI0 = deg2rad( $PHI0 ); my $RadLAM0 = deg2rad( $LAM0 ); my $af0 = $a * $f0; my $bf0 = $b * $f0; my $e2 = ( $af0**2 - $bf0**2 ) / $af0**2; my $n = ( $af0 - $bf0 ) / ( $af0 + $bf0 ); my $nu = $af0 / ( sqrt( 1 - ( $e2 * sin( $RadPHI )**2 ) ) ); my $rho = ( $nu * ( 1 - $e2 ) ) / ( 1 - ( $e2 * sin( $RadPHI )**2 ) ); my $eta2 = ( $nu / $rho ) - 1; my $p = $RadLAM - $RadLAM0; my $M = _marc( $bf0, $n, $RadPHI0, $RadPHI ); my $I = $M + $n0; my $II = ( $nu / 2 ) * ( sin( $RadPHI ) ) * ( cos( $RadPHI ) ); my $III = ( ( $nu / 24 ) * ( sin( $RadPHI ) ) * ( cos( $RadPHI )**3 ) ) * ( 5 - ( tan( $RadPHI )**2 ) + ( 9 * $eta2 ) ); my $IIIA = ( ( $nu / 720 ) * ( sin( $RadPHI ) ) * ( cos( $RadPHI )**5 ) ) * ( 61 - ( 58 * ( tan( $RadPHI )**2 ) ) + ( tan( $RadPHI )**4 ) ); return $I + ( $p**2 * $II ) + ( $p**4 * $III ) + ( $p**6 * $IIIA ); } sub _en2lat { my ( $East, $North, $a, $b, $e0, $n0, $f0, $PHI0, $LAM0 ) = @_; my $RadPHI0 = deg2rad( $PHI0 ); my $RadLAM0 = deg2rad( $LAM0 ); my $af0 = $a * $f0; my $bf0 = $b * $f0; my $e2 = ( $af0**2 - $bf0**2 ) / $af0**2; my $n = ( $af0 - $bf0 ) / ( $af0 + $bf0 ); my $Et = $East - $e0; my $PHId = _init_lat( $North, $n0, $af0, $RadPHI0, $n, $bf0 ); my $nu = $af0 / ( sqrt( 1 - ( $e2 * ( sin( $PHId )**2 ) ) ) ); my $rho = ( $nu * ( 1 - $e2 ) ) / ( 1 - ( $e2 * sin( $PHId )**2 ) ); my $eta2 = ( $nu / $rho ) - 1; my $VII = ( tan( $PHId ) ) / ( 2 * $rho * $nu ); my $VIII = ( ( tan( $PHId ) ) / ( 24 * $rho * $nu**3 ) ) * ( 5 + ( 3 * ( tan( $PHId )**2 ) ) + $eta2 - ( 9 * $eta2 * ( tan( $PHId )**2 ) ) ); my $IX = ( ( tan( $PHId ) ) / ( 720 * $rho * $nu**5 ) ) * ( 61 + ( 90 * ( ( tan( $PHId ) ) ^ 2 ) ) + ( 45 * ( tan( $PHId )**4 ) ) ); return ( 180 / pi ) * ( $PHId - ( $Et**2 * $VII ) + ( $Et**4 * $VIII ) - ( ( $Et**6 ) * $IX ) ); } sub _en2lon { my ( $East, $North, $a, $b, $e0, $n0, $f0, $PHI0, $LAM0 ) = @_; my $RadPHI0 = deg2rad( $PHI0 ); my $RadLAM0 = deg2rad( $LAM0 ); my $af0 = $a * $f0; my $bf0 = $b * $f0; my $e2 = ( $af0**2 - $bf0**2 ) / $af0**2; my $n = ( $af0 - $bf0 ) / ( $af0 + $bf0 ); my $Et = $East - $e0; my $PHId = _init_lat( $North, $n0, $af0, $RadPHI0, $n, $bf0 ); my $nu = $af0 / ( sqrt( 1 - ( $e2 * ( sin( $PHId )**2 ) ) ) ); my $rho = ( $nu * ( 1 - $e2 ) ) / ( 1 - ( $e2 * sin( $PHId )**2 ) ); my $eta2 = ( $nu / $rho ) - 1; my $X = ( cos( $PHId )**-1 ) / $nu; my $XI = ( ( cos( $PHId )**-1 ) / ( 6 * $nu**3 ) ) * ( ( $nu / $rho ) + ( 2 * ( tan( $PHId )**2 ) ) ); my $XII = ( ( cos( $PHId )**-1 ) / ( 120 * $nu**5 ) ) * ( 5 + ( 28 * ( tan( $PHId )**2 ) ) + ( 24 * ( tan( $PHId )**4 ) ) ); my $XIIA = ( ( cos( $PHId )**-1 ) / ( 5040 * $nu**7 ) ) * ( 61 + ( 662 * ( tan( $PHId )**2 ) ) + ( 1320 * ( tan( $PHId )**4 ) ) + ( 720 * ( tan( $PHId )**6 ) ) ); return ( 180 / pi ) * ( $RadLAM0 + ( $Et * $X ) - ( $Et**3 * $XI ) + ( $Et**5 * $XII ) - ( $Et**7 * $XIIA ) ); } sub _init_lat { my ( $North, $n0, $afo, $PHI0, $n, $bfo ) = @_; my $PHI1 = ( ( $North - $n0 ) / $afo ) + $PHI0; my $M = _marc( $bfo, $n, $PHI0, $PHI1 ); my $PHI2 = ( ( $North - $n0 - $M ) / $afo ) + $PHI1; while ( abs( $North - $n0 - $M ) > 0.00001 ) { $PHI2 = ( ( $North - $n0 - $M ) / $afo ) + $PHI1; $M = _marc( $bfo, $n, $PHI0, $PHI2 ); $PHI1 = $PHI2; } return $PHI2; } sub _marc { my ( $bf0, $n, $PHI0, $PHI ) = @_; return $bf0 * ( ( ( 1 + $n + ( ( 5 / 4 ) * $n**2 ) + ( ( 5 / 4 ) * $n**3 ) ) * ( $PHI - $PHI0 ) ) - ( ( ( 3 * $n ) + ( 3 * $n**2 ) + ( ( 21 / 8 ) * $n**3 ) ) * ( sin( $PHI - $PHI0 ) ) * ( cos( $PHI + $PHI0 ) ) ) + ( ( ( ( 15 / 8 ) * $n**2 ) + ( ( 15 / 8 ) * $n**3 ) ) * ( sin( 2 * ( $PHI - $PHI0 ) ) ) * ( cos( 2 * ( $PHI + $PHI0 ) ) ) ) - ( ( ( 35 / 24 ) * $n**3 ) * ( sin( 3 * ( $PHI - $PHI0 ) ) ) * ( cos( 3 * ( $PHI + $PHI0 ) ) ) ) ); } 1; __END__ =head1 BUGS Please report any bugs or feature requests to C, or through the web interface at L. =head1 AUTHOR Andy Armstrong C<< >> Code gratefully stolen from L http://svn.geograph.org.uk/svn/branches/british-isles/libs/geograph \ /conversionslatlong.class.php =head1 LICENCE AND COPYRIGHT Copyright (c) 2009, Andy Armstrong C<< >>. This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See L.