File Coverage

blib/lib/geo/ecef.pm
Criterion Covered Total %
statement 56 56 100.0
branch 19 28 67.8
condition 4 6 66.6
subroutine 15 15 100.0
pod 4 4 100.0
total 98 109 89.9


line stmt bran cond sub pod time code
1             package geo::ecef;
2              
3              
4             =head1 NAME
5              
6             geo::ecef - Functions for calculating ecef coordinates from lla
7              
8             =head1 SYNOPSIS
9              
10             use geo::ecef;
11             ($lat,$lon,height)=(60,45,0); # Decimal degrees and height in meters
12             $x=X($lat,$lon,height);
13             $y=Y($lat,$lon,height);
14             $z=Z($lat,$lon,height);
15              
16             # proj4 output
17            
18             @p4=split/\s/,'30d44\'15.43"N 55d16\'12.33"E';
19             $lat=p2dd($p4[0]);
20             $lon=p2dd($p4[1]);
21              
22              
23             =head1 DESCRIPTION
24              
25             geo::ecef contains functions for calculating the X,Y and Z coordinates in the
26             ecef (earth centered earth fixed) coordinate system from latitude, longitude
27             and height information. The wgs84 ellepsoide is used as a basis for the
28             calculation.
29              
30             In addition, geo::ecef contains an utilty function to read output from the
31             proj4 projection utility
32              
33             The formulaes were found at http://www.u-blox.ch.
34              
35             =head2 Methods
36              
37              
38             =cut
39              
40              
41              
42 4     4   101016 use strict;
  4         11  
  4         201  
43             our $VERSION='1.0.0';
44 4     4   21 use Exporter;
  4         6  
  4         171  
45 4     4   3987 use Math::Big qw/sin cos pi/;
  4         335298  
  4         339  
46 4     4   45 use Math::BigFloat;
  4         10  
  4         20  
47             #Math::BigFloat->precision(20);
48             # This causes the checking modules to fail, why?
49             our @ISA = qw/ Exporter /;
50             our @EXPORT = qw / X Y Z p2dd/;
51             our @EXPORTOK = qw /$A $B/;
52 4     4   2549 use Carp;
  4         8  
  4         282  
53 4     4   23 use warnings;
  4         7  
  4         3156  
54             my($PI,$F,$E,$E2);
55             $PI=pi(20);
56             # WGS84:
57             our($A,$B);
58             our($ellps)="WGS84";
59             if ($ellps eq "WGS84"){
60             $A =new Math::BigFloat '6378137.00000';
61             $B =new Math::BigFloat '6356752.31424518';
62             $F =new Math::BigFloat 1/'298.257223563';
63              
64             }
65             if ($ellps eq "NAD83"){
66             $A =new Math::BigFloat '6378137.00000';
67             $B =new Math::BigFloat '6356752.0000';
68             $F =new Math::BigFloat 1/'298.57';
69             }
70              
71             if ($ellps eq "TEST"){
72             $A =2;
73             $B =1.5;
74             $F = 1-(1.5/2);
75             }
76             # http://www.ga.gov.au/nmd/geodesy/datums/wgs.jsp
77             $E = sqrt(($A**2 - $B**2)/$A**2);
78             $E2= sqrt(($A**2 - $B**2)/$B**2);
79              
80             # This program converts LLA (latitude, longitude, altitude) locations
81             # into the ECEF (Earth Centered Earth Fixed) coordinate system
82             # The formulaes were found at http://www.u-blox.ch.
83              
84             # Morten Sickel,
85             # Norwegian Radiation Protection Authority
86              
87             # parameters from WGS84
88              
89             sub _checkdeg{
90 29     29   61 my ($lat,$lon)=@_;
91 29 100       217 return('invalid latitude (outside [-90,90])') if abs($lat) > $PI;
92 25 100       3597 return('invalid longitude (outside [-180,180]') if abs($lon) > 2*$PI;
93 21         9592 return(undef);
94             }
95              
96             sub _dtr{
97 40     40   274 return $_[0]/180.0*$PI
98             }
99              
100             sub _N{
101 20     20   13312 my $lat=shift;
102 20         81 my $n= $A / sqrt(1-(($E**2)*(sin($lat)**2)));
103 20         392658 return($n);
104             }
105              
106              
107              
108             =head3 X
109              
110             X($lat,$lon,$height,$rad)
111              
112             calculates the ecef X-coordinate from the latitude, longitude and height.
113             The lat and lon is considered beeing degrees, unless $rad is set to a true
114             value, in which case they are considered to be radianes.
115              
116              
117             =cut
118              
119              
120             sub X{
121 5     5 1 76444 my ($lat,$lon,$h,$rad)=@_;
122 5 50       29 $lat=_dtr($lat) unless $rad;
123 5 50       2559 $lon=_dtr($lon) unless $rad;
124 5 50       2224 carp(_checkdeg($lat,$lon)) if _checkdeg($lat,$lon);
125 5         15 return (_N($lat)+$h)*cos($lat)*cos($lon);
126             }
127              
128             =head3 Y
129              
130             calculates the Y-coordinate, else similiar to X()
131              
132             =cut
133              
134             sub Y{
135 7     7 1 426730 my ($lat,$lon,$h,$rad)=@_;
136 7 50       55 $lat=_dtr($lat) unless $rad;
137 7 50       3385 $lon=_dtr($lon) unless $rad;
138 7 50       3108 carp(_checkdeg($lat,$lon)) if _checkdeg($lat,$lon);
139 7         65 return (_N($lat)+$h)*cos($lat)*sin($lon);
140             }
141              
142             =head3 Z
143              
144             calculates the Z-coordinate, else similiar to X()
145              
146             =cut
147              
148             sub Z{
149 8     8 1 239575 my ($lat,$lon,$h,$rad)=@_;
150 8 50       59 $lat=_dtr($lat) unless $rad;
151 8 50       4141 $lon=_dtr($lon) unless $rad;
152 8 50       3543 carp(_checkdeg($lat,$lon)) if _checkdeg($lat,$lon);
153 8         41 return((( $B**2/ $A**2 * _N($lat))+$h)*sin($lat));
154             }
155              
156             =head3 p2dd()
157              
158             p2dd takes a latitude or longitude as output by proj4 and converts it to
159             decimal degrees. The input format is on the form 71d3'56.623"N. undef is
160             returned for invalid values, i.e. min or sec >= 60.
161              
162             =cut
163              
164             #'
165              
166             sub p2dd{
167             # DMS Coordinates as given out by proj V4
168             # eg 71d3'56.623"N
169             # or 71d3'N
170             # or 71dN
171             # May use NSEW, S and W coordinates should be negative
172 17     17 1 52 my $coord=shift;
173 17         127 $coord=~/(\d+)d(\d+\'(\d+\.?\d*\")?)?(E|W|N|S)/;
174 4     4   23 no warnings;
  4         7  
  4         503  
175             # May get some undefined value varnings.
176 17         47 my $deg= $1;
177 17 100       100 my $min= $2 if $2*1;
178 17 100       61 my $sek= $3 if $3*1;
179 17   33     67 my $quad= $4 || $3 || $2;
180 17         47 my $sign= 2*($quad =~tr/NE/NE/)-1;
181             # Sign is 1 for N or E, -1 for others i.e S or W
182 17 100 100     130 return undef if ($min>=60 || $sek >= 60);
183             # This should not happen when reading proj4 data, but just in case
184 13         165 $deg=$sign*($deg+$min/60+$sek/3600);
185 4     4   18 use warnings;
  4         6  
  4         257  
186             }
187              
188             1;
189              
190             =head1 LICENCE
191              
192             This program is free software; you may redistribute it and/or modify it
193             under the same terms as Perl itself.
194              
195             =head1 AUTHORS
196              
197             (c) by Morten Sickel http://sickel.net/ 2005
198              
199             =cut