test_utm.cpp
Go to the documentation of this file.
1 /* $Id: 47db7b5025f4ca800961335f30ef67b18cfe69ca $ */
2 
3 /*********************************************************************
4 * Software License Agreement (BSD License)
5 *
6 * Copyright (c) 2011 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 <sstream>
38 #include <gtest/gtest.h>
39 #include <angles/angles.h>
40 #include "geodesy/utm.h"
41 
42 
44 // Utility functions
46 
47 // check that two UTM points are near each other
49  const geodesy::UTMPoint &pt2,
50  double abs_err)
51 {
52  EXPECT_NEAR(pt1.easting, pt2.easting, abs_err);
53  EXPECT_NEAR(pt1.northing, pt2.northing, abs_err);
54  EXPECT_NEAR(pt1.altitude, pt2.altitude, abs_err);
55  EXPECT_EQ(pt1.zone, pt2.zone);
56  EXPECT_EQ(pt1.band, pt2.band);
57 }
58 
60 // Test cases
62 
63 // Test null constructor
64 TEST(UTMPoint, nullConstructor)
65 {
67 
68  EXPECT_EQ(pt.easting, 0.0);
69  EXPECT_EQ(pt.northing, 0.0);
70  EXPECT_TRUE(geodesy::is2D(pt));
71  EXPECT_EQ(pt.zone, 0);
72  EXPECT_EQ(pt.band, ' ');
73  EXPECT_FALSE(geodesy::isValid(pt));
74 }
75 
76 // Test 2D constructor
77 TEST(UTMPoint, flatConstructor)
78 {
79  double e = 1000.0;
80  double n = 2400.0;
81  uint8_t z = 14;
82  char b = 'R';
83  geodesy::UTMPoint pt(e, n, z, b);
84 
85  EXPECT_TRUE(geodesy::is2D(pt));
86  EXPECT_TRUE(geodesy::isValid(pt));
87 
88  EXPECT_EQ(pt.easting, e);
89  EXPECT_EQ(pt.northing, n);
90  EXPECT_EQ(pt.zone, z);
91  EXPECT_EQ(pt.band, b);
92 }
93 
94 // Test 3D constructor
95 TEST(UTMPoint, hasAltitude)
96 {
97  double e = 1000.0;
98  double n = 2400.0;
99  double a = 200.0;
100  uint8_t z = 14;
101  char b = 'R';
102  geodesy::UTMPoint pt(e, n, a, z, b);
103 
104  EXPECT_FALSE(geodesy::is2D(pt));
105  EXPECT_TRUE(geodesy::isValid(pt));
106 
107  EXPECT_EQ(pt.easting, e);
108  EXPECT_EQ(pt.northing, n);
109  EXPECT_EQ(pt.zone, z);
110  EXPECT_EQ(pt.band, b);
111 }
112 
113 // Test copy constructor
114 TEST(UTMPoint, copyConstructor)
115 {
116  double e = 1000.0;
117  double n = 2400.0;
118  double a = 200.0;
119  uint8_t z = 14;
120  char b = 'R';
121  geodesy::UTMPoint pt1(e, n, a, z, b);
122  geodesy::UTMPoint pt2(pt1);
123 
124  EXPECT_FALSE(geodesy::is2D(pt2));
125  EXPECT_TRUE(geodesy::isValid(pt2));
126 
127  EXPECT_EQ(pt2.easting, e);
128  EXPECT_EQ(pt2.northing, n);
129  EXPECT_EQ(pt2.zone, z);
130  EXPECT_EQ(pt2.band, b);
131 }
132 
133 // Test UTM point constructor from WGS 84
134 TEST(UTMPoint, fromLatLong)
135 {
136  // University of Texas, Austin, Pickle Research Campus
137  double lat = 30.385315;
138  double lon = -97.728524;
139  double alt = 209.0;
140 
141  geographic_msgs::GeoPoint ll;
142  ll.latitude = lat;
143  ll.longitude = lon;
144  ll.altitude = alt;
145 
146  // create UTM from point
147  geodesy::UTMPoint pt(ll);
148 
149  double e = 622159.34;
150  double n = 3362168.30;
151  uint8_t z = 14;
152  char b = 'R';
153  double abs_err = 0.01;
154 
155  EXPECT_FALSE(geodesy::is2D(pt));
156  EXPECT_TRUE(geodesy::isValid(pt));
157 
158  EXPECT_NEAR(pt.easting, e, abs_err);
159  EXPECT_NEAR(pt.northing, n, abs_err);
160  EXPECT_NEAR(pt.altitude, alt, abs_err);
161  EXPECT_EQ(pt.zone, z);
162  EXPECT_EQ(pt.band, b);
163 }
164 
165 // Test zone numbers
166 TEST(UTMPoint, testZones)
167 {
169  pt.band = 'X'; // supply a valid band letter
170 
171  pt.zone = 0;
172  EXPECT_FALSE(geodesy::isValid(pt));
173 
174  pt.zone = 61;
175  EXPECT_FALSE(geodesy::isValid(pt));
176 
177  pt.zone = 255;
178  EXPECT_FALSE(geodesy::isValid(pt));
179 
180  // these should all work
181  for (uint8_t b = 1; b <= 60; ++b)
182  {
183  pt.zone = b;
184  EXPECT_TRUE(geodesy::isValid(pt));
185  }
186 }
187 
188 // Test band letters
189 TEST(UTMPoint, testBands)
190 {
192  pt.zone = 14; // supply a valid zone number
193  EXPECT_FALSE(geodesy::isValid(pt));
194 
195  pt.band = '9';
196  EXPECT_FALSE(geodesy::isValid(pt));
197 
198  pt.band = ';';
199  EXPECT_FALSE(geodesy::isValid(pt));
200 
201  pt.band = 'I';
202  EXPECT_FALSE(geodesy::isValid(pt));
203 
204  pt.band = 'O';
205  EXPECT_FALSE(geodesy::isValid(pt));
206 
207  pt.band = 'Y';
208  EXPECT_FALSE(geodesy::isValid(pt));
209 
210  pt.band = 'r';
211  EXPECT_FALSE(geodesy::isValid(pt));
212 
213  // this should work
214  pt.band = 'X';
215  EXPECT_TRUE(geodesy::isValid(pt));
216 }
217 
218 // Test null pose constructor
219 TEST(UTMPose, nullConstructor)
220 {
221  geodesy::UTMPose pose;
222 
223  EXPECT_TRUE(geodesy::is2D(pose));
224  EXPECT_FALSE(geodesy::isValid(pose));
225 }
226 
227 // Test pose constructor from point and quaternion
228 TEST(UTMPose, pointQuaternionConstructor)
229 {
230  double e = 1000.0;
231  double n = 2400.0;
232  double a = 200.0;
233  uint8_t z = 14;
234  char b = 'R';
235  geodesy::UTMPoint pt(e, n, a, z, b);
236 
237  geometry_msgs::Quaternion q; // identity quaternion
238  q.x = 1.0;
239  q.y = 0.0;
240  q.z = 0.0;
241  q.w = 0.0;
242  geodesy::UTMPose pose(pt, q);
243 
244  EXPECT_FALSE(geodesy::is2D(pose));
245  EXPECT_TRUE(geodesy::isValid(pose));
246 
247  EXPECT_EQ(pose.position.easting, pt.easting);
248  EXPECT_EQ(pose.position.northing, pt.northing);
249  EXPECT_EQ(pose.position.altitude, pt.altitude);
250  EXPECT_EQ(pose.position.zone, pt.zone);
251  EXPECT_EQ(pose.position.band, pt.band);
252 
253  EXPECT_EQ(pose.orientation.w, q.w);
254  EXPECT_EQ(pose.orientation.x, q.x);
255  EXPECT_EQ(pose.orientation.y, q.y);
256  EXPECT_EQ(pose.orientation.z, q.z);
257 }
258 
259 // Test pose quaternion validation
260 TEST(UTMPose, quaternionValidation)
261 {
262  double e = 1000.0;
263  double n = 2400.0;
264  double a = 200.0;
265  uint8_t z = 14;
266  char b = 'R';
267  geodesy::UTMPoint pt(e, n, a, z, b);
268 
269  // identity quaternion
270  geometry_msgs::Quaternion q;
271  q.x = 1.0;
272  q.y = 0.0;
273  q.z = 0.0;
274  q.w = 0.0;
275  geodesy::UTMPose pose1(pt, q);
276  EXPECT_TRUE(geodesy::isValid(pose1));
277 
278  // valid quaternion
279  q.x = 0.7071;
280  q.y = 0.0;
281  q.z = 0.0;
282  q.w = 0.7071;
283  geodesy::UTMPose pose2(pt, q);
284  EXPECT_TRUE(geodesy::isValid(pose2));
285 
286  // quaternion not normalized
287  q.x = 0.8071;
288  q.y = 0.0;
289  q.z = 0.0;
290  q.w = 0.8071;
291  geodesy::UTMPose pose3(pt, q);
292  EXPECT_FALSE(geodesy::isValid(pose3));
293 
294  // zero quaternion (not normalized)
295  q.x = 0.0;
296  q.y = 0.0;
297  q.z = 0.0;
298  q.w = 0.0;
299  geodesy::UTMPose pose4(pt, q);
300  EXPECT_FALSE(geodesy::isValid(pose4));
301 }
302 
303 // Test UTM pose conversion
304 TEST(UTMConvert, fromUtmPoseToLatLongAndBack)
305 {
306  double e = 500000.0; // central meridian of each zone
307  double n = 1000.0;
308  double alt = 100.0;
309  char b = 'N';
310 
311  // try every possible zone of longitude
312  for (uint8_t z = 1; z <= 60; ++z)
313  {
314  for (unsigned int heading = 0; heading < 360; heading++)
315  {
316  geodesy::UTMPose ps1(geodesy::UTMPoint(e, n, alt, z, b),
318  geographic_msgs::GeoPose ll;
319  convert(ps1, ll);
320  geodesy::UTMPose ps2;
321  convert(ll, ps2);
322 
323  EXPECT_TRUE(geodesy::isValid(ps1));
324  EXPECT_TRUE(geodesy::isValid(ps2));
325  check_utm_near(ps1.position, ps2.position, 0.000001);
326  EXPECT_DOUBLE_EQ(tf::getYaw(ps1.orientation), tf::getYaw(ps2.orientation));
327  }
328  }
329 }
330 
331 // Test conversion from UTM to WGS 84 and back
332 TEST(UTMConvert, fromUtmToLatLongAndBack)
333 {
334  double e = 500000.0; // central meridian of each zone
335  double n = 1000.0;
336  double alt = 100.0;
337  char b = 'N';
338 
339  // try every possible zone of longitude
340  for (uint8_t z = 1; z <= 60; ++z)
341  {
342  geodesy::UTMPoint pt1(e, n, alt, z, b);
343  geographic_msgs::GeoPoint ll;
344  convert(pt1, ll);
345  geodesy::UTMPoint pt2;
346  convert(ll, pt2);
347 
348  EXPECT_TRUE(geodesy::isValid(pt1));
349  EXPECT_TRUE(geodesy::isValid(pt2));
350  check_utm_near(pt1, pt2, 0.000001);
351  }
352 }
353 
354 // Test conversion from WGS 84 to UTM and back
355 TEST(UTMConvert, fromLatLongToUtmAndBack)
356 {
357  double alt = 100.0;
358 
359  // Try every possible degree of latitude and longitude. Avoid the
360  // international date line. Even though the converted longitude is
361  // close, it may end up less than -180 and hence inValid().
362  for (double lon = -179.5; lon < 180.0; lon += 1.0)
363  {
364  for (double lat = -80.0; lat <= 84.0; lat += 1.0)
365  {
366  geographic_msgs::GeoPoint pt1(geodesy::toMsg(lat, lon, alt));
367  EXPECT_TRUE(geodesy::isValid(pt1));
368 
369  geodesy::UTMPoint utm(pt1);
370  EXPECT_TRUE(geodesy::isValid(utm));
371 
372  geographic_msgs::GeoPoint pt2(geodesy::toMsg(utm));
373  EXPECT_TRUE(geodesy::isValid(pt2));
374 
375  EXPECT_NEAR(pt1.latitude, pt2.latitude, 0.0000001);
376  EXPECT_NEAR(pt1.longitude, pt2.longitude, 0.0000012);
377  EXPECT_NEAR(pt1.altitude, pt2.altitude, 0.000001);
378  }
379  }
380 }
381 
382 // Test conversion from WGS 84 to UTM and back at international date line
383 TEST(UTMConvert, internationalDateLine)
384 {
385  double alt = 100.0;
386  double lon = -180.0;
387 
388  for (double lat = -80.0; lat <= 84.0; lat += 1.0)
389  {
390  geographic_msgs::GeoPoint pt1(geodesy::toMsg(lat, lon, alt));
391  EXPECT_TRUE(geodesy::isValid(pt1));
392 
393  geodesy::UTMPoint utm;
394  geodesy::fromMsg(pt1, utm);
395  EXPECT_TRUE(geodesy::isValid(utm));
396 
397  geographic_msgs::GeoPoint pt2(geodesy::toMsg(utm));
398  EXPECT_TRUE(geodesy::isValid(pt2));
399  EXPECT_NEAR(pt1.latitude, pt2.latitude, 0.0000001);
400  EXPECT_NEAR(pt1.altitude, pt2.altitude, 0.000001);
401 
402  if (pt2.longitude - pt1.longitude > 359.0)
403  {
404  // pt2 seems to be slightly across the international date
405  // line from pt2, so de-normalize it
406  pt2.longitude -= 360.0;
407  }
408  EXPECT_NEAR(pt1.longitude, pt2.longitude, 0.0000012);
409  }
410 }
411 
412 // Test point output stream operator
413 TEST(OStream, point)
414 {
415  geodesy::UTMPoint pt1;
416  std::ostringstream out1;
417  out1 << pt1;
418  std::string expected("(0, 0, nan [0 ])");
419  EXPECT_EQ(out1.str(), expected);
420 
421  geodesy::UTMPoint pt2(622159.338, 3362168.303, 209, 14, 'R');
422  std::ostringstream out2;
423  out2 << pt2;
424  expected = "(622159.338, 3362168.303, 209 [14R])";
425  EXPECT_EQ(out2.str(), expected);
426 }
427 
428 // Test pose output stream operator
429 TEST(OStream, pose)
430 {
431  geodesy::UTMPoint pt(1000.0, 2400.0, 200.0, 14, 'R');
432  geometry_msgs::Quaternion q;
433  q.w = 1.0;
434  q.x = 0.0;
435  q.y = 0.0;
436  q.z = 0.0;
437  geodesy::UTMPose pose(pt, q);
438 
439  std::ostringstream out;
440  out << pose;
441  std::string expected("(1000, 2400, 200 [14R]), ([0, 0, 0], 1)");
442  EXPECT_EQ(out.str(), expected);
443 }
444 
445 TEST(ForceUTMZone, point)
446 {
447 
448  geographic_msgs::GeoPoint zone2, zone3;
449  zone2.latitude=24.02;
450  zone2 = geodesy::toMsg(24.02, 5.999);
451  zone3 = geodesy::toMsg(24.02, 6.001);
452  geodesy::UTMPoint pt2, pt3, pt4;
453  geodesy::fromMsg(zone2, pt2);
454  geodesy::fromMsg(zone3, pt3);
455 
456  EXPECT_FALSE(geodesy::sameGridZone(pt2, pt3) );
457 
458  double diffx = pt2.easting - pt3.easting;
459  double diffy = pt2.northing - pt3.northing;
460  double distance = std::sqrt(diffx*diffx + diffy*diffy);
461 
462  //Now force the pt3 into pt2's grid zone
463  geodesy::fromMsg(zone3, pt4, true, pt2.band, pt2.zone);
464  diffx = pt2.easting - pt4.easting;
465  diffy = pt2.northing - pt4.northing;
466  double distance2 = std::sqrt(diffx*diffx + diffy*diffy);
467  ROS_INFO("Prev Distance %f, Actual Distance %f", distance, distance2);
468  EXPECT_LT(distance2, distance);
469 }
470 
471 // Run all the tests that were declared with TEST()
472 int main(int argc, char **argv)
473 {
474  testing::InitGoogleTest(&argc, argv);
475 
476  // run the tests in this thread
477  return RUN_ALL_TESTS();
478 }
char band
MGRS latitude band letter.
Definition: utm.h:130
Universal Transverse Mercator coordinates.
static double getYaw(const Quaternion &bt_q)
void fromMsg(const geographic_msgs::GeoPoint &from, UTMPoint &to, const bool &force_zone=false, const char &band='A', const uint8_t &zone=0)
int main(int argc, char **argv)
Definition: test_utm.cpp:472
TFSIMD_FORCE_INLINE tfScalar distance(const Vector3 &v) const
double northing
northing within grid zone [meters]
Definition: utm.h:127
double altitude
altitude above ellipsoid [meters] or NaN
Definition: utm.h:128
static bool sameGridZone(const UTMPoint &pt1, const UTMPoint &pt2)
Definition: utm.h:234
geometry_msgs::Quaternion orientation
Definition: utm.h:173
uint8_t zone
UTM longitude zone number.
Definition: utm.h:129
static double from_degrees(double degrees)
#define ROS_INFO(...)
TFSIMD_FORCE_INLINE const tfScalar & z() const
TEST(UTMPoint, nullConstructor)
Definition: test_utm.cpp:64
static bool is2D(const UTMPoint &pt)
Definition: utm.h:186
static geometry_msgs::Quaternion createQuaternionMsgFromYaw(double yaw)
bool isValid(const UTMPoint &pt)
TFSIMD_FORCE_INLINE tfScalar distance2(const Vector3 &v) const
void check_utm_near(const geodesy::UTMPoint &pt1, const geodesy::UTMPoint &pt2, double abs_err)
Definition: test_utm.cpp:48
double easting
easting within grid zone [meters]
Definition: utm.h:126
UTMPoint position
Definition: utm.h:172
void convert(const From &from, To &to)
Definition: wgs84.h:227
geographic_msgs::GeoPoint toMsg(const UTMPoint &from)


geodesy
Author(s): Jack O'Quin
autogenerated on Sat May 8 2021 02:29:15