utm_conversions.cpp
Go to the documentation of this file.
00001 /* $Id: 8b94f2e1fa9389bb9b9229465157c45e84029c98 $ */
00002 
00003 /*********************************************************************
00004 * Software License Agreement (BSD License)
00005 *
00006 *  Copyright (C) 2011 Chuck Gantz, Jack O'Quin
00007 *  All rights reserved.
00008 *
00009 *  Redistribution and use in source and binary forms, with or without
00010 *  modification, are permitted provided that the following conditions
00011 *  are met:
00012 *
00013 *   * Redistributions of source code must retain the above copyright
00014 *     notice, this list of conditions and the following disclaimer.
00015 *   * Redistributions in binary form must reproduce the above
00016 *     copyright notice, this list of conditions and the following
00017 *     disclaimer in the documentation and/or other materials provided
00018 *     with the distribution.
00019 *   * Neither the name of the author nor other contributors may be
00020 *     used to endorse or promote products derived from this software
00021 *     without specific prior written permission.
00022 *
00023 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00024 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00025 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00026 *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00027 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00028 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00029 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00030 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00031 *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00032 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00033 *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00034 *  POSSIBILITY OF SUCH DAMAGE.
00035 *********************************************************************/
00036 
00037 //#include <exception>
00038 #include <ros/ros.h>
00039 #include <angles/angles.h>
00040 #include <geodesy/utm.h>
00041 
00055 namespace geodesy
00056 {
00057 
00058 // WGS84 Parameters
00059 #define WGS84_A         6378137.0               // major axis
00060 #define WGS84_B         6356752.31424518        // minor axis
00061 #define WGS84_F         0.0033528107            // ellipsoid flattening
00062 #define WGS84_E         0.0818191908            // first eccentricity
00063 #define WGS84_EP        0.0820944379            // second eccentricity
00064 
00065 // UTM Parameters
00066 #define UTM_K0          0.9996                  // scale factor
00067 #define UTM_FE          500000.0                // false easting
00068 #define UTM_FN_N        0.0           // false northing, northern hemisphere
00069 #define UTM_FN_S        10000000.0    // false northing, southern hemisphere
00070 #define UTM_E2          (WGS84_E*WGS84_E)       // e^2
00071 #define UTM_E4          (UTM_E2*UTM_E2)         // e^4
00072 #define UTM_E6          (UTM_E4*UTM_E2)         // e^6
00073 #define UTM_EP2         (UTM_E2/(1-UTM_E2))     // e'^2
00074 
00075 
00082 static char UTMBand(double Lat, double Lon)
00083 {
00084   char LetterDesignator;
00085 
00086   if     ((84 >= Lat) && (Lat >= 72))  LetterDesignator = 'X';
00087   else if ((72 > Lat) && (Lat >= 64))  LetterDesignator = 'W';
00088   else if ((64 > Lat) && (Lat >= 56))  LetterDesignator = 'V';
00089   else if ((56 > Lat) && (Lat >= 48))  LetterDesignator = 'U';
00090   else if ((48 > Lat) && (Lat >= 40))  LetterDesignator = 'T';
00091   else if ((40 > Lat) && (Lat >= 32))  LetterDesignator = 'S';
00092   else if ((32 > Lat) && (Lat >= 24))  LetterDesignator = 'R';
00093   else if ((24 > Lat) && (Lat >= 16))  LetterDesignator = 'Q';
00094   else if ((16 > Lat) && (Lat >= 8))   LetterDesignator = 'P';
00095   else if (( 8 > Lat) && (Lat >= 0))   LetterDesignator = 'N';
00096   else if (( 0 > Lat) && (Lat >= -8))  LetterDesignator = 'M';
00097   else if ((-8 > Lat) && (Lat >= -16)) LetterDesignator = 'L';
00098   else if((-16 > Lat) && (Lat >= -24)) LetterDesignator = 'K';
00099   else if((-24 > Lat) && (Lat >= -32)) LetterDesignator = 'J';
00100   else if((-32 > Lat) && (Lat >= -40)) LetterDesignator = 'H';
00101   else if((-40 > Lat) && (Lat >= -48)) LetterDesignator = 'G';
00102   else if((-48 > Lat) && (Lat >= -56)) LetterDesignator = 'F';
00103   else if((-56 > Lat) && (Lat >= -64)) LetterDesignator = 'E';
00104   else if((-64 > Lat) && (Lat >= -72)) LetterDesignator = 'D';
00105   else if((-72 > Lat) && (Lat >= -80)) LetterDesignator = 'C';
00106   // '_' is an error flag, the Latitude is outside the UTM limits
00107   else LetterDesignator = ' ';
00108 
00109   return LetterDesignator;
00110 }
00111 
00119 geographic_msgs::GeoPoint toMsg(const UTMPoint &from)
00120 {
00121   //remove 500,000 meter offset for longitude
00122   double x = from.easting - 500000.0;
00123   double y = from.northing;
00124 
00125   double k0 = UTM_K0;
00126   double a = WGS84_A;
00127   double eccSquared = UTM_E2;
00128   double eccPrimeSquared;
00129   double e1 = (1-sqrt(1-eccSquared))/(1+sqrt(1-eccSquared));
00130   double N1, T1, C1, R1, D, M;
00131   double LongOrigin;
00132   double mu, phi1Rad;
00133 
00134   if ((from.band - 'N') < 0)
00135     {
00136       //point is in southern hemisphere
00137       //remove 10,000,000 meter offset used for southern hemisphere
00138       y -= 10000000.0;
00139     }
00140 
00141   //+3 puts origin in middle of zone
00142   LongOrigin = (from.zone - 1)*6 - 180 + 3;
00143   eccPrimeSquared = (eccSquared)/(1-eccSquared);
00144 
00145   M = y / k0;
00146   mu = M/(a*(1-eccSquared/4-3*eccSquared*eccSquared/64
00147              -5*eccSquared*eccSquared*eccSquared/256));
00148 
00149   phi1Rad = mu + ((3*e1/2-27*e1*e1*e1/32)*sin(2*mu) 
00150                   + (21*e1*e1/16-55*e1*e1*e1*e1/32)*sin(4*mu)
00151                   + (151*e1*e1*e1/96)*sin(6*mu));
00152 
00153   N1 = a/sqrt(1-eccSquared*sin(phi1Rad)*sin(phi1Rad));
00154   T1 = tan(phi1Rad)*tan(phi1Rad);
00155   C1 = eccPrimeSquared*cos(phi1Rad)*cos(phi1Rad);
00156   R1 = a*(1-eccSquared)/pow(1-eccSquared*sin(phi1Rad)*sin(phi1Rad), 1.5);
00157   D = x/(N1*k0);
00158 
00159   // function result
00160   geographic_msgs::GeoPoint to;
00161   to.altitude = from.altitude;
00162   to.latitude =
00163     phi1Rad - ((N1*tan(phi1Rad)/R1)
00164                *(D*D/2
00165                  -(5+3*T1+10*C1-4*C1*C1-9*eccPrimeSquared)*D*D*D*D/24
00166                  +(61+90*T1+298*C1+45*T1*T1-252*eccPrimeSquared
00167                    -3*C1*C1)*D*D*D*D*D*D/720));
00168   to.latitude = angles::to_degrees(to.latitude);
00169   to.longitude = ((D-(1+2*T1+C1)*D*D*D/6
00170                    +(5-2*C1+28*T1-3*C1*C1+8*eccPrimeSquared+24*T1*T1)
00171                    *D*D*D*D*D/120)
00172                   / cos(phi1Rad));
00173   to.longitude = LongOrigin + angles::to_degrees(to.longitude);
00174 
00175   // Normalize latitude and longitude to valid ranges.
00176   normalize(to);
00177   return to;
00178 }
00179 
00187 void fromMsg(const geographic_msgs::GeoPoint &from, UTMPoint &to)
00188 {
00189   double Lat = from.latitude;
00190   double Long = from.longitude;
00191 
00192   double a = WGS84_A;
00193   double eccSquared = UTM_E2;
00194   double k0 = UTM_K0;
00195 
00196   double LongOrigin;
00197   double eccPrimeSquared;
00198   double N, T, C, A, M;
00199         
00200   // Make sure the longitude is between -180.00 .. 179.9
00201   // (JOQ: this is broken for Long < -180, do a real normalize)
00202   double LongTemp = (Long+180)-int((Long+180)/360)*360-180;
00203   double LatRad = angles::from_degrees(Lat);
00204   double LongRad = angles::from_degrees(LongTemp);
00205   double LongOriginRad;
00206 
00207   to.altitude = from.altitude;
00208   to.zone = int((LongTemp + 180)/6) + 1;
00209   
00210   if( Lat >= 56.0 && Lat < 64.0 && LongTemp >= 3.0 && LongTemp < 12.0 )
00211     to.zone = 32;
00212 
00213   // Special zones for Svalbard
00214   if( Lat >= 72.0 && Lat < 84.0 ) 
00215     {
00216       if(      LongTemp >= 0.0  && LongTemp <  9.0 ) to.zone = 31;
00217       else if( LongTemp >= 9.0  && LongTemp < 21.0 ) to.zone = 33;
00218       else if( LongTemp >= 21.0 && LongTemp < 33.0 ) to.zone = 35;
00219       else if( LongTemp >= 33.0 && LongTemp < 42.0 ) to.zone = 37;
00220     }
00221   // +3 puts origin in middle of zone
00222   LongOrigin = (to.zone - 1)*6 - 180 + 3; 
00223   LongOriginRad = angles::from_degrees(LongOrigin);
00224 
00225   // compute the UTM band from the latitude
00226   to.band = UTMBand(Lat, LongTemp);
00227 #if 0
00228   if (to.band == ' ')
00229     throw std::range_error;
00230 #endif
00231 
00232   eccPrimeSquared = (eccSquared)/(1-eccSquared);
00233 
00234   N = a/sqrt(1-eccSquared*sin(LatRad)*sin(LatRad));
00235   T = tan(LatRad)*tan(LatRad);
00236   C = eccPrimeSquared*cos(LatRad)*cos(LatRad);
00237   A = cos(LatRad)*(LongRad-LongOriginRad);
00238 
00239   M = a*((1 - eccSquared/4 - 3*eccSquared*eccSquared/64
00240           - 5*eccSquared*eccSquared*eccSquared/256) * LatRad 
00241          - (3*eccSquared/8 + 3*eccSquared*eccSquared/32
00242             + 45*eccSquared*eccSquared*eccSquared/1024)*sin(2*LatRad)
00243          + (15*eccSquared*eccSquared/256
00244             + 45*eccSquared*eccSquared*eccSquared/1024)*sin(4*LatRad) 
00245          - (35*eccSquared*eccSquared*eccSquared/3072)*sin(6*LatRad));
00246         
00247   to.easting = (double)
00248     (k0*N*(A+(1-T+C)*A*A*A/6
00249            + (5-18*T+T*T+72*C-58*eccPrimeSquared)*A*A*A*A*A/120)
00250      + 500000.0);
00251 
00252   to.northing = (double)
00253     (k0*(M+N*tan(LatRad)
00254          *(A*A/2+(5-T+9*C+4*C*C)*A*A*A*A/24
00255            + (61-58*T+T*T+600*C-330*eccPrimeSquared)*A*A*A*A*A*A/720)));
00256 
00257   if(Lat < 0)
00258     {
00259       //10000000 meter offset for southern hemisphere
00260       to.northing += 10000000.0;
00261     }
00262 }
00263 
00265 bool isValid(const UTMPoint &pt)
00266 {
00267   if (pt.zone < 1 || pt.zone > 60)
00268     return false;
00269 
00270   if (!isupper(pt.band) || pt.band == 'I' || pt.band == 'O')
00271     return false;
00272 
00273   // The Universal Polar Stereographic bands are not currently
00274   // supported.  When they are: A, B, Y and Z will be valid (and the
00275   // zone number will be ignored).
00276   if (pt.band < 'C' || pt.band > 'X')
00277     return false;
00278 
00279   return true;
00280 }
00281  
00283 UTMPoint::UTMPoint(const geographic_msgs::GeoPoint &pt)
00284 {
00285   fromMsg(pt, *this);
00286 }
00287 
00295 void fromMsg(const geographic_msgs::GeoPose &from, UTMPose &to)
00296 {
00297   fromMsg(from.position, to.position);
00298   to.orientation = from.orientation;
00299 }
00300 
00302 bool isValid(const UTMPose &pose)
00303 {
00304   if (!isValid(pose.position))
00305     return false;
00306 
00307   // check that orientation quaternion is normalized
00308   double len2 = (pose.orientation.x * pose.orientation.x
00309                  + pose.orientation.y * pose.orientation.y
00310                  + pose.orientation.z * pose.orientation.z
00311                  + pose.orientation.w * pose.orientation.w);
00312   return fabs(len2 - 1.0) <= tf::QUATERNION_TOLERANCE;
00313 }
00314 
00315 } // end namespace geodesy


geodesy
Author(s): Jack O'Quin
autogenerated on Mon Oct 6 2014 00:09:35