utm_conversions.cpp
Go to the documentation of this file.
1 /* $Id: 8b94f2e1fa9389bb9b9229465157c45e84029c98 $ */
2 
3 /*********************************************************************
4 * Software License Agreement (BSD License)
5 *
6 * Copyright (C) 2011 Chuck Gantz, Jack O'Quin
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
13 * * Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * * Redistributions in binary form must reproduce the above
16 * copyright notice, this list of conditions and the following
17 * disclaimer in the documentation and/or other materials provided
18 * with the distribution.
19 * * Neither the name of the author nor other contributors may be
20 * used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
33 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 * POSSIBILITY OF SUCH DAMAGE.
35 *********************************************************************/
36 
37 //#include <exception>
38 #include <ros/ros.h>
39 #include <angles/angles.h>
40 #include <geodesy/utm.h>
41 
55 namespace geodesy
56 {
57 
58 // WGS84 Parameters
59 #define WGS84_A 6378137.0 // major axis
60 #define WGS84_B 6356752.31424518 // minor axis
61 #define WGS84_F 0.0033528107 // ellipsoid flattening
62 #define WGS84_E 0.0818191908 // first eccentricity
63 #define WGS84_EP 0.0820944379 // second eccentricity
64 
65 // UTM Parameters
66 #define UTM_K0 0.9996 // scale factor
67 #define UTM_FE 500000.0 // false easting
68 #define UTM_FN_N 0.0 // false northing, northern hemisphere
69 #define UTM_FN_S 10000000.0 // false northing, southern hemisphere
70 #define UTM_E2 (WGS84_E*WGS84_E) // e^2
71 #define UTM_E4 (UTM_E2*UTM_E2) // e^4
72 #define UTM_E6 (UTM_E4*UTM_E2) // e^6
73 #define UTM_EP2 (UTM_E2/(1-UTM_E2)) // e'^2
74 
75 
82 static char UTMBand(double Lat, double Lon)
83 {
84  char LetterDesignator;
85 
86  if ((84 >= Lat) && (Lat >= 72)) LetterDesignator = 'X';
87  else if ((72 > Lat) && (Lat >= 64)) LetterDesignator = 'W';
88  else if ((64 > Lat) && (Lat >= 56)) LetterDesignator = 'V';
89  else if ((56 > Lat) && (Lat >= 48)) LetterDesignator = 'U';
90  else if ((48 > Lat) && (Lat >= 40)) LetterDesignator = 'T';
91  else if ((40 > Lat) && (Lat >= 32)) LetterDesignator = 'S';
92  else if ((32 > Lat) && (Lat >= 24)) LetterDesignator = 'R';
93  else if ((24 > Lat) && (Lat >= 16)) LetterDesignator = 'Q';
94  else if ((16 > Lat) && (Lat >= 8)) LetterDesignator = 'P';
95  else if (( 8 > Lat) && (Lat >= 0)) LetterDesignator = 'N';
96  else if (( 0 > Lat) && (Lat >= -8)) LetterDesignator = 'M';
97  else if ((-8 > Lat) && (Lat >= -16)) LetterDesignator = 'L';
98  else if((-16 > Lat) && (Lat >= -24)) LetterDesignator = 'K';
99  else if((-24 > Lat) && (Lat >= -32)) LetterDesignator = 'J';
100  else if((-32 > Lat) && (Lat >= -40)) LetterDesignator = 'H';
101  else if((-40 > Lat) && (Lat >= -48)) LetterDesignator = 'G';
102  else if((-48 > Lat) && (Lat >= -56)) LetterDesignator = 'F';
103  else if((-56 > Lat) && (Lat >= -64)) LetterDesignator = 'E';
104  else if((-64 > Lat) && (Lat >= -72)) LetterDesignator = 'D';
105  else if((-72 > Lat) && (Lat >= -80)) LetterDesignator = 'C';
106  // '_' is an error flag, the Latitude is outside the UTM limits
107  else LetterDesignator = ' ';
108 
109  return LetterDesignator;
110 }
111 
119 geographic_msgs::GeoPoint toMsg(const UTMPoint &from)
120 {
121  //remove 500,000 meter offset for longitude
122  double x = from.easting - 500000.0;
123  double y = from.northing;
124 
125  double k0 = UTM_K0;
126  double a = WGS84_A;
127  double eccSquared = UTM_E2;
128  double eccPrimeSquared;
129  double e1 = (1-sqrt(1-eccSquared))/(1+sqrt(1-eccSquared));
130  double N1, T1, C1, R1, D, M;
131  double LongOrigin;
132  double mu, phi1Rad;
133 
134  if ((from.band - 'N') < 0)
135  {
136  //point is in southern hemisphere
137  //remove 10,000,000 meter offset used for southern hemisphere
138  y -= 10000000.0;
139  }
140 
141  //+3 puts origin in middle of zone
142  LongOrigin = (from.zone - 1)*6 - 180 + 3;
143  eccPrimeSquared = (eccSquared)/(1-eccSquared);
144 
145  M = y / k0;
146  mu = M/(a*(1-eccSquared/4-3*eccSquared*eccSquared/64
147  -5*eccSquared*eccSquared*eccSquared/256));
148 
149  phi1Rad = mu + ((3*e1/2-27*e1*e1*e1/32)*sin(2*mu)
150  + (21*e1*e1/16-55*e1*e1*e1*e1/32)*sin(4*mu)
151  + (151*e1*e1*e1/96)*sin(6*mu));
152 
153  N1 = a/sqrt(1-eccSquared*sin(phi1Rad)*sin(phi1Rad));
154  T1 = tan(phi1Rad)*tan(phi1Rad);
155  C1 = eccPrimeSquared*cos(phi1Rad)*cos(phi1Rad);
156  R1 = a*(1-eccSquared)/pow(1-eccSquared*sin(phi1Rad)*sin(phi1Rad), 1.5);
157  D = x/(N1*k0);
158 
159  // function result
160  geographic_msgs::GeoPoint to;
161  to.altitude = from.altitude;
162  to.latitude =
163  phi1Rad - ((N1*tan(phi1Rad)/R1)
164  *(D*D/2
165  -(5+3*T1+10*C1-4*C1*C1-9*eccPrimeSquared)*D*D*D*D/24
166  +(61+90*T1+298*C1+45*T1*T1-252*eccPrimeSquared
167  -3*C1*C1)*D*D*D*D*D*D/720));
168  to.latitude = angles::to_degrees(to.latitude);
169  to.longitude = ((D-(1+2*T1+C1)*D*D*D/6
170  +(5-2*C1+28*T1-3*C1*C1+8*eccPrimeSquared+24*T1*T1)
171  *D*D*D*D*D/120)
172  / cos(phi1Rad));
173  to.longitude = LongOrigin + angles::to_degrees(to.longitude);
174 
175  // Normalize latitude and longitude to valid ranges.
176  normalize(to);
177  return to;
178 }
179 
187 void fromMsg(const geographic_msgs::GeoPoint &from, UTMPoint &to)
188 {
189  double Lat = from.latitude;
190  double Long = from.longitude;
191 
192  double a = WGS84_A;
193  double eccSquared = UTM_E2;
194  double k0 = UTM_K0;
195 
196  double LongOrigin;
197  double eccPrimeSquared;
198  double N, T, C, A, M;
199 
200  // Make sure the longitude is between -180.00 .. 179.9
201  // (JOQ: this is broken for Long < -180, do a real normalize)
202  double LongTemp = (Long+180)-int((Long+180)/360)*360-180;
203  double LatRad = angles::from_degrees(Lat);
204  double LongRad = angles::from_degrees(LongTemp);
205  double LongOriginRad;
206 
207  to.altitude = from.altitude;
208  to.zone = int((LongTemp + 180)/6) + 1;
209 
210  if( Lat >= 56.0 && Lat < 64.0 && LongTemp >= 3.0 && LongTemp < 12.0 )
211  to.zone = 32;
212 
213  // Special zones for Svalbard
214  if( Lat >= 72.0 && Lat < 84.0 )
215  {
216  if( LongTemp >= 0.0 && LongTemp < 9.0 ) to.zone = 31;
217  else if( LongTemp >= 9.0 && LongTemp < 21.0 ) to.zone = 33;
218  else if( LongTemp >= 21.0 && LongTemp < 33.0 ) to.zone = 35;
219  else if( LongTemp >= 33.0 && LongTemp < 42.0 ) to.zone = 37;
220  }
221  // +3 puts origin in middle of zone
222  LongOrigin = (to.zone - 1)*6 - 180 + 3;
223  LongOriginRad = angles::from_degrees(LongOrigin);
224 
225  // compute the UTM band from the latitude
226  to.band = UTMBand(Lat, LongTemp);
227 #if 0
228  if (to.band == ' ')
229  throw std::range_error;
230 #endif
231 
232  eccPrimeSquared = (eccSquared)/(1-eccSquared);
233 
234  N = a/sqrt(1-eccSquared*sin(LatRad)*sin(LatRad));
235  T = tan(LatRad)*tan(LatRad);
236  C = eccPrimeSquared*cos(LatRad)*cos(LatRad);
237  A = cos(LatRad)*(LongRad-LongOriginRad);
238 
239  M = a*((1 - eccSquared/4 - 3*eccSquared*eccSquared/64
240  - 5*eccSquared*eccSquared*eccSquared/256) * LatRad
241  - (3*eccSquared/8 + 3*eccSquared*eccSquared/32
242  + 45*eccSquared*eccSquared*eccSquared/1024)*sin(2*LatRad)
243  + (15*eccSquared*eccSquared/256
244  + 45*eccSquared*eccSquared*eccSquared/1024)*sin(4*LatRad)
245  - (35*eccSquared*eccSquared*eccSquared/3072)*sin(6*LatRad));
246 
247  to.easting = (double)
248  (k0*N*(A+(1-T+C)*A*A*A/6
249  + (5-18*T+T*T+72*C-58*eccPrimeSquared)*A*A*A*A*A/120)
250  + 500000.0);
251 
252  to.northing = (double)
253  (k0*(M+N*tan(LatRad)
254  *(A*A/2+(5-T+9*C+4*C*C)*A*A*A*A/24
255  + (61-58*T+T*T+600*C-330*eccPrimeSquared)*A*A*A*A*A*A/720)));
256 
257  if(Lat < 0)
258  {
259  //10000000 meter offset for southern hemisphere
260  to.northing += 10000000.0;
261  }
262 }
263 
265 bool isValid(const UTMPoint &pt)
266 {
267  if (pt.zone < 1 || pt.zone > 60)
268  return false;
269 
270  if (!isupper(pt.band) || pt.band == 'I' || pt.band == 'O')
271  return false;
272 
273  // The Universal Polar Stereographic bands are not currently
274  // supported. When they are: A, B, Y and Z will be valid (and the
275  // zone number will be ignored).
276  if (pt.band < 'C' || pt.band > 'X')
277  return false;
278 
279  return true;
280 }
281 
283 UTMPoint::UTMPoint(const geographic_msgs::GeoPoint &pt)
284 {
285  fromMsg(pt, *this);
286 }
287 
295 void fromMsg(const geographic_msgs::GeoPose &from, UTMPose &to)
296 {
297  fromMsg(from.position, to.position);
298  to.orientation = from.orientation;
299 }
300 
302 bool isValid(const UTMPose &pose)
303 {
304  if (!isValid(pose.position))
305  return false;
306 
307  // check that orientation quaternion is normalized
308  double len2 = (pose.orientation.x * pose.orientation.x
309  + pose.orientation.y * pose.orientation.y
310  + pose.orientation.z * pose.orientation.z
311  + pose.orientation.w * pose.orientation.w);
312  return fabs(len2 - 1.0) <= tf::QUATERNION_TOLERANCE;
313 }
314 
315 } // end namespace geodesy
char band
MGRS latitude band letter.
Definition: utm.h:130
Definition: utm.h:68
Universal Transverse Mercator coordinates.
static const double QUATERNION_TOLERANCE
TFSIMD_FORCE_INLINE const tfScalar & y() const
double northing
northing within grid zone [meters]
Definition: utm.h:127
double altitude
altitude above ellipsoid [meters] or NaN
Definition: utm.h:128
geometry_msgs::Quaternion orientation
Definition: utm.h:173
#define WGS84_A
uint8_t zone
UTM longitude zone number.
Definition: utm.h:129
static double from_degrees(double degrees)
#define UTM_K0
static double to_degrees(double radians)
TFSIMD_FORCE_INLINE const tfScalar & x() const
void fromMsg(const geographic_msgs::GeoPoint &from, UTMPoint &to)
static void normalize(UTMPoint &pt)
Definition: utm.h:204
#define UTM_E2
bool isValid(const UTMPoint &pt)
double easting
easting within grid zone [meters]
Definition: utm.h:126
UTMPoint position
Definition: utm.h:172
geographic_msgs::GeoPoint toMsg(const UTMPoint &from)
static char UTMBand(double Lat, double Lon)


geodesy
Author(s): Jack O'Quin
autogenerated on Thu Jun 6 2019 19:56:59