UTMPoint.cc
Go to the documentation of this file.
1 
9 #include <utl/UTMPoint.h>
10 #include <utl/Point.h>
11 #include <utl/CoordinateSystem.h>
12 #include <utl/ErrorLogger.h>
13 #include <utl/AugerUnits.h>
14 #include <utl/MathConstants.h>
15 
16 #include <boost/tuple/tuple.hpp>
17 #include <boost/tuple/tuple_io.hpp>
18 
19 #include <sstream>
20 #include <cmath>
21 
22 using namespace utl;
23 
24 
25 // some private constants for the implementation
26 namespace {
27  // utm zones
28  const double utmZoneWidth = 6*deg;
29  const double utmZoneOffset = 180*deg; // added to lattitude to move
30  // angles from range
31  // -180..180 to 0..360.
32 
33  const char utmBandLetter[] = "CDEFGHJKLMNPQRSTUVWX";
34  const int numberOfUTMBands = sizeof(utmBandLetter) / sizeof(char) - 1;
35  const double widthOfUTMBand = 8*deg;
36  [[maybe_unused]] const double southernmostUTMBand = -80*deg;
37  const char firstNorthernBand = 'N'; // 11th zone
38 
39  // utm definitions from transformation
40  double k0 = 0.9996;
41 }
42 
43 
44 Point
46  const
47 {
48  double latitude;
49  double longitude;
50  double height;
51  boost::tie(latitude, longitude, height) = GetGeodeticCoordinates();
52  const Point pt =
53  fEllipsoid->LatitudeLongitudeHeightToPoint(latitude, longitude, height);
54 
55  if (theCS)
56  pt.TransformTo(theCS);
57 
58  return pt;
59 }
60 
61 
66 boost::tuple<double, double, double>
68  const
69 {
70  //converts UTM coords to lat/long. Equations from USGS Bulletin
71  //1532 East Longitudes are positive, West longitudes are negative.
72  //North latitudes are positive, South latitudes are negative. Lat and
73  //Long are in Auger units.
74 
75  const double a = fEllipsoid->GetEquatorialRadius();
76  const double eccSquared = fEllipsoid->GetEccentricity2();
77 
78  const double e1 = (1 - sqrt(1-eccSquared)) / (1 + sqrt(1-eccSquared));
79 
80  const double x = fEasting - 500000; //remove 500,000 meter offset for longitude
81  double y = fNorthing;
82 
83  const bool inNorthernHemisphere = fBand >= firstNorthernBand;
84  if (!inNorthernHemisphere)
85  y -= 10000000; // remove 10,000,000 meter offset used
86  // for southern hemisphere
87 
88  const double LongitudeOrigin = (fZone - 0.5) * utmZoneWidth - utmZoneOffset;
89 
90  const double eccPrimeSquared = eccSquared / (1-eccSquared);
91  const double e4 = eccSquared*eccSquared;
92  const double e6 = e4*eccSquared;
93  const double e8 = e4*e4;
94  const double e1_2 = e1*e1;
95 
96  const double M = y / k0;
97  const double mu = M / (a * (1 - eccSquared/4 - 3*e4/64 - 5*e6/256 - 175*e8/16384));
98  const double mur = mu/rad;
99  const double phi1 = mur
100  + (3*e1/2 - 27*e1_2*e1/32)*sin(2*mur)
101  + (21*e1_2/16 - 55*e1_2*e1_2/32)*sin(4*mur)
102  + (151*e1*e1_2/96)*sin(6*mur)
103  + (1097*e1_2*e1_2/512)*sin(8*mur);
104 
105  const double phi1r = phi1/rad;
106  const double cosPhi1 = cos(phi1r);
107  const double sinPhi1 = sin(phi1r);
108  const double tanPhi1 = sinPhi1/cosPhi1;
109 
110  const double N1 = a / sqrt(1 - eccSquared * sinPhi1*sinPhi1);
111  const double T1 = tanPhi1*tanPhi1;
112  const double C1 = eccPrimeSquared * cosPhi1*cosPhi1;
113  const double R1 = a * (1-eccSquared) / pow(1 - eccSquared * sinPhi1*sinPhi1, 1.5);
114  const double D = x / (N1*k0);
115  const double D2 = D*D;
116  const double D4 = D2*D2;
117  const double C1_2 = C1*C1;
118  const double T1_2 = T1*T1;
119 
120  const double latitude = phi1
121  - N1*tanPhi1/R1 * (
122  D2/2
123  - (5 + 3*T1 + 10*C1 - 4*C1_2 - 9*eccPrimeSquared) * D4 / 24
124  + (61 + 90*T1 + 298*C1 + 45*T1_2 - 252*eccPrimeSquared - 3*C1_2) * D2*D4 / 720
125  );
126 
127  const double longitude =
128  (D
129  - (1 + 2*T1 + C1) * D*D2 / 6
130  + (5 - 2*C1 + 28*T1 - 3*C1_2 + 8*eccPrimeSquared + 24*T1_2) * D*D4 / 120
131  ) / cosPhi1 + LongitudeOrigin;
132 
133  return boost::make_tuple(latitude, longitude, fHeight);
134 }
135 
136 
137 /***************************************************************************
138  * Private, internal functions
139  ***************************************************************************/
140 
141 void
143 {
144  if (*fEllipsoid != *theUTMPoint.fEllipsoid) {
145  std::ostringstream msg;
146  msg << "DATUM conversions not implemented in UTMPoint constructor";
147  FATAL(msg);
148  throw UTMException(msg.str());
149  }
150 
151  fNorthing = theUTMPoint.fNorthing;
152  fEasting = theUTMPoint.fEasting;
153  fHeight = theUTMPoint.fHeight;
154  fZone = theUTMPoint.fZone;
155  fBand = theUTMPoint.fBand;
156 }
157 
158 
159 void
161 {
162  double latitude;
163  double longitude;
164  double height;
165 
166  boost::tie(latitude, longitude, height) =
168 
169  fHeight = height;
170  SetFromLatitudeLongitude(latitude, longitude);
171 }
172 
173 
178 void
179 UTMPoint::SetFromLatitudeLongitude(const double theLatitude,
180  const double theLongitude)
181 {
182  fZone = ZoneFromLatitudeLongitude(theLatitude, theLongitude);
183  fBand = BandFromLatitude(theLatitude);
184 
185  const double LongitudeOrigin = (fZone-0.5)*utmZoneWidth - utmZoneOffset;
186 
187  //converts lat/long to UTM coords. Equations from USGS Bulletin
188  //1532 East Longitudes are positive, West longitudes are negative.
189  //North latitudes are positive, South latitudes are negative. Lat and
190  //Long are in Auger Units
191 
192  const double a = fEllipsoid->GetEquatorialRadius();
193  const double eccSquared = fEllipsoid->GetEccentricity2();
194 
195  const double lat = theLatitude / rad;
196  const double cosLatitude = cos(lat);
197  const double sinLatitude = sin(lat);
198  const double tanLatitude = sinLatitude / cosLatitude;
199 
200  const double eccPrimeSquared = eccSquared / (1 - eccSquared);
201 
202  const double N = a / sqrt(1 - eccSquared * sinLatitude*sinLatitude);
203  const double T = tanLatitude*tanLatitude;
204  const double C = eccPrimeSquared * cosLatitude*cosLatitude;
205  const double A = cosLatitude * (theLongitude/rad - LongitudeOrigin/rad);
206 
207  const double e4 = eccSquared*eccSquared;
208  const double e6 = e4 * eccSquared;
209  const double e8 = e4*e4;
210 
211  const double c1 =
212  1
213  - eccSquared / 4
214  - 3 * e4 / 64
215  - 5 * e6 / 256
216  - 175 * e8 / 16384;
217  const double c2 =
218  - 3 * eccSquared / 8
219  - 3 * e4 / 32
220  - 45 * e6 / 1024
221  - 105 * e8 / 4096;
222  const double c3 =
223  15 * e4 / 256
224  + 45 * e6 / 1024
225  + 525 * e8 / 16384;
226  const double c4 =
227  - 35 * e6 / 3072
228  - 175 * e8 / 12288;
229  const double c5 = 315 * e8 / 131072;
230 
231  const double M = a *
232  (c1 * lat
233  + c2 * sin(2*lat)
234  + c3 * sin(4*lat)
235  + c4 * sin(6*lat)
236  + c5 * sin(8*lat)
237  );
238 
239  const double A2 = A*A;
240  const double A3 = A2*A;
241  const double T2 = T*T;
242 
243  fEasting = k0 * N * (A + (1-T+C) * A3/6
244  + (5 - 18*T + T2 + 72*C - 58*eccPrimeSquared) * A2*A3 / 120)
245  + 500000;
246 
247  fNorthing = k0 * (M + N * tanLatitude *
248  (A2/2 + (5 - T + 9*C + 4*C*C) * A*A3 / 24
249  + (61 - 58*T + T2 + 600*C - 330*eccPrimeSquared) * A3*A3 / 720));
250 
251  if (theLatitude < 0)
252  fNorthing += 10000000; //10 000 000 meter offset for southern hemisphere
253 }
254 
255 
256 void
258 {
259  if (fZone < eZoneMin || fZone > eZoneMax) {
260  std::ostringstream msg;
261  msg << "Zone out of bounds: " << fZone
262  << " not in " << eZoneMin << ".." << eZoneMax << ".";
263  FATAL(msg);
264  throw UTMZoneException(msg.str());
265  }
266 }
267 
268 
269 /***************************************************************************
270  * static, private, internal functions
271  ***************************************************************************/
272 
273 int
274 UTMPoint::ZoneFromLatitudeLongitude(const double theLatitude,
275  const double theLongitude)
276 {
277  using std::floor;
278  int zone = int(floor(theLongitude / utmZoneWidth)) + eZoneMax/2 + 1;
279 
280  // southern Norway zone exception
281  if (56*deg <= theLatitude && theLatitude < 64*deg &&
282  3*deg <= theLongitude && theLatitude < 12*deg) {
283  zone = 32;
284  }
285  else if (72*deg <= theLatitude && theLatitude < 84*deg) {
286  // Svalbard (Norway) zone exceptions
287  if (0 <= theLongitude && theLongitude < 9*deg)
288  zone = 31;
289  else if (9*deg <= theLongitude && theLongitude < 21*deg)
290  zone = 33;
291  else if (21*deg <= theLongitude && theLongitude < 33*deg)
292  zone = 35;
293  else if (33*deg <= theLongitude && theLongitude < 42*deg)
294  zone = 37;
295  }
296 
297  return zone;
298 }
299 
300 
301 char
302 UTMPoint::BandFromLatitude(const double theLatitude)
303 {
304  using std::floor;
305  int index = int(floor(theLatitude / widthOfUTMBand)) + numberOfUTMBands/2;
306  if (0 <= index && index < numberOfUTMBands)
307  return utmBandLetter[index];
308  else if (72*deg <= theLatitude && theLatitude < 84*deg)
309  return 'X';
310  else {
311  std::ostringstream msg;
312  msg << "Latitude " << theLatitude/deg
313  << " deg out of range [-80 .. 84]. "
314  "Cannot calculate band letter." << std::endl;
315  ERROR(msg);
316  throw UTMZoneException(msg.str());
317  return ' '; // to keep the compiler happy
318  }
319 }
320 
321 
322 /***************************************************************************
323  * friends and related functions
324  ***************************************************************************/
325 
326 std::ostream&
327 utl::operator<<(std::ostream& s, const UTMPoint& p)
328 {
329  s << p.GetCoordinates();
330  return s;
331 }
Point object.
Definition: Point.h:32
double GetEccentricity2() const
Get eccentricity.
double fHeight
Definition: UTMPoint.h:279
boost::tuple< double, double, double, int, char > GetCoordinates() const
Get norting, easting, height, zone, and band.
Definition: UTMPoint.h:232
Class to hold and convert a point in geodetic coordinates.
Definition: UTMPoint.h:40
static int ZoneFromLatitudeLongitude(const double theLatitude, const double theLongitude)
Calculate zone number from latitude and longitude.
Definition: UTMPoint.cc:274
void SetFromOtherDatum(const UTMPoint &theUTMPoint)
Datum transformation.
Definition: UTMPoint.cc:142
constexpr double rad
Definition: AugerUnits.h:137
Point LatitudeLongitudeHeightToPoint(double const latitude, double const longitude, double const height) const
Convert Lat/Long/Height to Point.
#define FATAL(message)
Macro for logging fatal messages.
Definition: ErrorLogger.h:167
double pow(const double x, const unsigned int i)
Report attempts to use invalid UTM zone.
Definition: UTMPoint.h:300
Stream & operator<<(Stream &s, MessageLoggerConfig &mlc)
Applies the configuration to the given stream.
constexpr double deg
Definition: AugerUnits.h:140
void VerifyZone()
verify zone and throw exception if out of bounds
Definition: UTMPoint.cc:257
boost::shared_ptr< const CoordinateTransformer > CoordinateSystemPtr
Shared pointer for coordinate systems.
constexpr double s
Definition: AugerUnits.h:163
double GetEquatorialRadius() const
Get equatorial radius ( )
Triple PointToLatitudeLongitudeHeight(const Point &thePoint) const
Convert Point to Lat/Long/Height.
Maximum UTM zone number.
Definition: UTMPoint.h:45
int fZone
zone runs from 1 to 60
Definition: UTMPoint.h:281
double fNorthing
Definition: UTMPoint.h:277
Report problems in UTM handling.
Definition: UTMPoint.h:288
double fEasting
Definition: UTMPoint.h:278
const ReferenceEllipsoid * fEllipsoid
Definition: UTMPoint.h:284
char fBand
Letter indicating band, C-X, w/o O, I.
Definition: UTMPoint.h:283
void SetFromLatitudeLongitude(const double theLatitude, const double theLongitude)
Construction from geodetic Latitude and Longitude (height identical)
Definition: UTMPoint.cc:179
static char BandFromLatitude(const double theLatitude)
Calculate band letter from latitude and longitude.
Definition: UTMPoint.cc:302
void SetFromCartesian(const Point &thePoint)
Construction from Cartesian coordinates.
Definition: UTMPoint.cc:160
Point GetPoint(const CoordinateSystemPtr &theCS=CoordinateSystemPtr()) const
Get a cartesian point from an UTMPoint.
Definition: UTMPoint.cc:45
#define ERROR(message)
Macro for logging error messages.
Definition: ErrorLogger.h:165
boost::tuple< double, double, double > GetGeodeticCoordinates() const
Get geodetic latitude, longitude, height.
Definition: UTMPoint.cc:67
Minimum UTM zone number.
Definition: UTMPoint.h:44

, generated on Tue Sep 26 2023.