GeographicLib  2.6
Cart3Convert.cpp
Go to the documentation of this file.
1 /**
2  * \file Cart3Convert.cpp
3  * \brief Command line utility for computing geodesics on a triaxial ellipsoid
4  *
5  * Copyright (c) Charles Karney (2024-2025) <karney@alum.mit.edu> and licensed
6  * under the MIT/X11 License. For more information, see
7  * https://geographiclib.sourceforge.io/
8  *
9  * See the <a href="Cart3Convert.1.html">man page</a> for usage information.
10  **********************************************************************/
11 
12 // Counterpart of CartConvert
13 
14 // Usual flags
15 // -r (x, y, z to ellipsoidal to geographic)
16 // -e (supplement with -t)
17 // -w longfirst
18 // -p prec
19 // -h help
20 // -d dms
21 // -: colon
22 // --help
23 // --version
24 // --comment-delimiter
25 // + other I/O related flags
26 
27 // New flags
28 // -E ellipsoidal coords (default)
29 // -G geodetic
30 // -P parametric
31 // -C geocentric
32 // -3 include height (only for -E and -G)
33 // -D include direction (only for -E without -3)
34 
35 // Can't specify -3 and -D together
36 // Height for -G has its usual definition
37 // Height for -E is the increase in the minor semiaxis for the confocal
38 // ellipsoid (H = u - c).
39 
40 #include <iostream>
41 #include <iomanip>
42 #include <string>
43 #include <sstream>
44 #include <fstream>
45 #include <GeographicLib/DMS.hpp>
47 #include <GeographicLib/Angle.hpp>
49 
50 #include "Cart3Convert.usage"
51 
52 int main(int argc, const char* const argv[]) {
53  try {
54  using namespace GeographicLib;
55  using namespace Triaxial;
57  using real = Math::real;
58  using ang = Angle;
59  using vec3 = Ellipsoid3::vec3;
60  using coord = Cartesian3::coord;
61  enum { GEODETIC, PARAMETRIC, GEOCENTRIC, ELLIPSOIDAL };
62  coord mode = coord::ELLIPSOIDAL;
63  bool threed = false, direction = false, reverse = false, dms = false,
64  longfirst = false, randompts = false;
65  real
69  e2 = -1, k2 = 0, kp2 = 0;
70  int prec = 3, nrand = 0;
71  unsigned long long seed = 0;
72  std::string istring, ifile, ofile, cdelim;
73  char lsep = ';', dmssep = char(0);
74 
75  for (int m = 1; m < argc; ++m) {
76  std::string arg(argv[m]);
77  if (arg == "-E")
78  mode = coord::ELLIPSOIDAL;
79  else if (arg == "-G")
80  mode = coord::GEODETIC;
81  else if (arg == "-P")
82  mode = coord::PARAMETRIC;
83  else if (arg == "-C")
84  mode = coord::GEOCENTRIC;
85  else if (arg == "-GX")
86  mode = coord::GEODETIC_X;
87  else if (arg == "-PX")
88  mode = coord::PARAMETRIC_X;
89  else if (arg == "-CX")
90  mode = coord::GEOCENTRIC_X;
91  else if (arg == "-R") {
92  if (++m == argc) return usage(1, true);
93  try {
94  nrand = Utility::val<int>(std::string(argv[m]));
95  }
96  catch (const std::exception&) {
97  std::cerr << "Number of randoms " << argv[m] << " is not a number\n";
98  return 1;
99  }
100  randompts = true;
101  } else if (arg == "-3")
102  threed = true;
103  else if (arg == "-D")
104  direction = true;
105  else if (arg == "-r")
106  reverse = true;
107  else if (arg == "--seed") {
108  if (++m == argc) return usage(1, true);
109  try {
110  seed = Utility::val<unsigned long long>(std::string(argv[m]));
111  }
112  catch (const std::exception&) {
113  std::cerr << "Precision " << argv[m] << " is not a number\n";
114  return 1;
115  }
116  } else if (arg == "-t") {
117  if (m + 3 >= argc) return usage(1, true);
118  try {
119  a = Utility::val<real>(std::string(argv[m + 1]));
120  b = Utility::val<real>(std::string(argv[m + 2]));
121  c = Utility::val<real>(std::string(argv[m + 3]));
122  }
123  catch (const std::exception& e) {
124  std::cerr << "Error decoding arguments of -t: " << e.what() << "\n";
125  return 1;
126  }
127  e2 = -1;
128  m += 3;
129  } else if (arg == "-e") {
130  // Cayley ellipsoid sqrt([2,1,1/2]) is
131  // -e 1 3/2 1/3 2/3
132  if (m + 4 >= argc) return usage(1, true);
133  try {
134  b = Utility::val<real>(std::string(argv[m + 1]));
135  e2 = Utility::fract<real>(std::string(argv[m + 2]));
136  k2 = Utility::fract<real>(std::string(argv[m + 3]));
137  kp2 = Utility::fract<real>(std::string(argv[m + 4]));
138  }
139  catch (const std::exception& e) {
140  std::cerr << "Error decoding arguments of -e: " << e.what() << "\n";
141  return 1;
142  }
143  a = -1;
144  m += 4;
145  } else if (arg == "-d") {
146  dms = true;
147  dmssep = '\0';
148  } else if (arg == "-:") {
149  dms = true;
150  dmssep = ':';
151  } else if (arg == "-w")
152  longfirst = !longfirst;
153  else if (arg == "-p") {
154  if (++m == argc) return usage(1, true);
155  try {
156  prec = Utility::val<int>(std::string(argv[m]));
157  }
158  catch (const std::exception&) {
159  std::cerr << "Precision " << argv[m] << " is not a number\n";
160  return 1;
161  }
162  } else if (arg == "--input-string") {
163  if (++m == argc) return usage(1, true);
164  istring = argv[m];
165  } else if (arg == "--input-file") {
166  if (++m == argc) return usage(1, true);
167  ifile = argv[m];
168  } else if (arg == "--output-file") {
169  if (++m == argc) return usage(1, true);
170  ofile = argv[m];
171  } else if (arg == "--line-separator") {
172  if (++m == argc) return usage(1, true);
173  if (std::string(argv[m]).size() != 1) {
174  std::cerr << "Line separator must be a single character\n";
175  return 1;
176  }
177  lsep = argv[m][0];
178  } else if (arg == "--comment-delimiter") {
179  if (++m == argc) return usage(1, true);
180  cdelim = argv[m];
181  } else if (arg == "--version") {
182  std::cout << argv[0] << ": GeographicLib version "
183  << GEOGRAPHICLIB_VERSION_STRING << "\n";
184  return 0;
185  } else
186  return usage(!(arg == "-h" || arg == "--help"), arg != "--help");
187  }
188 
189  Cartesian3 tc(e2 >= 0 ? Ellipsoid3(b, e2, k2, kp2) : Ellipsoid3(a, b, c));
190 
191  if (randompts) {
192  if (threed) {
193  std::cerr << "Cannot specify -3 for random points\n";
194  return 1;
195  }
196  if (!ifile.empty() || !istring.empty()) {
197  std::cerr
198  << "Cannot specify --input-{string,file} with random points\n";
199  }
200  } else {
201  if (direction && threed) {
202  std::cerr << "Cannot specify both -3 and -D\n";
203  return 1;
204  }
205  }
206 
207  if (!ifile.empty() && !istring.empty()) {
208  std::cerr << "Cannot specify --input-string and --input-file together\n";
209  return 1;
210  }
211  if (ifile == "-") ifile.clear();
212  std::ifstream infile;
213  std::istringstream instring;
214  if (!ifile.empty()) {
215  infile.open(ifile.c_str());
216  if (!infile.is_open()) {
217  std::cerr << "Cannot open " << ifile << " for reading\n";
218  return 1;
219  }
220  } else if (!istring.empty()) {
221  std::string::size_type m = 0;
222  while (true) {
223  m = istring.find(lsep, m);
224  if (m == std::string::npos)
225  break;
226  istring[m] = '\n';
227  }
228  instring.str(istring);
229  }
230  std::istream* input = !ifile.empty() ? &infile :
231  (!istring.empty() ? &instring : &std::cin);
232 
233  std::ofstream outfile;
234  if (ofile == "-") ofile.clear();
235  if (!ofile.empty()) {
236  outfile.open(ofile.c_str());
237  if (!outfile.is_open()) {
238  std::cerr << "Cannot open " << ofile << " for writing\n";
239  return 1;
240  }
241  }
242  std::ostream* output = !ofile.empty() ? &outfile : &std::cout;
243 
244  // Max precision = 10: 0.1 nm in distance, 10^-15 deg (= 0.11 nm),
245  // 10^-11 sec (= 0.3 nm).
246  prec = std::min(10 + Math::extra_digits(), std::max(0, prec));
247  using std::round, std::log10, std::ceil, std::signbit;
248  int disprec = std::max(0, prec + int(round(log10(6400000/b)))),
249  angprec = prec + 5, vecprec = prec + 7;
250  if (randompts) {
251  // C a r t 3 C o n
252  unsigned long long s1 = 0x4361727433436f6eULL, s2 = seed;
253  if (seed == 0) {
254  s1 = std::random_device()();
255  s2 = std::random_device()();
256  }
257  std::seed_seq seq{s1, s2};
258  std::mt19937 g(seq);
259  for (int i = 0; i < nrand; ++i) {
260  vec3 r, v = {0,0,0};
261  if (direction)
262  tc.cart2rand(g, r, v);
263  else
264  tc.cart2rand(g, r);
265  if (!reverse) {
266  *output << Utility::str(r[0], disprec) << " "
267  << Utility::str(r[1], disprec) << " "
268  << Utility::str(r[2], disprec);
269  if (direction)
270  *output << " "
271  << Utility::str(v[0], vecprec) << " "
272  << Utility::str(v[1], vecprec) << " "
273  << Utility::str(v[2], vecprec);
274  } else {
275  ang bet, omg, alp;
276  tc.cart2toany(r, v, mode, bet, omg, alp);
277  *output << ang::LatLonString(bet, omg,
278  angprec, dms, dmssep, longfirst);
279  if (direction)
280  *output << " " << ang::AzimuthString(alp, angprec, dms, dmssep);
281  }
282  *output << "\n";
283  }
284  return 0;
285  }
286  std::string s, eol, sbet, somg, salp, sh, sx, sy, sz, svx, svy, svz, strc;
287  std::istringstream str;
288  int retval = 0;
289  vec3 r = {0,0,0}, v = {0,0,0};
290  ang bet, omg, alp;
291  real h = 0;
292  while (std::getline(*input, s)) {
293  try {
294  eol = "\n";
295  if (!cdelim.empty()) {
296  std::string::size_type m = s.find(cdelim);
297  if (m != std::string::npos) {
298  eol = " " + s.substr(m) + "\n";
299  s = s.substr(0, m);
300  }
301  }
302  // READ
303  str.clear(); str.str(s);
304  if (reverse) {
305  if (!(str >> sx >> sy >> sz))
306  throw GeographicErr("Incomplete input: " + s);
307  r[0] = Utility::val<real>(sx);
308  r[1] = Utility::val<real>(sy);
309  r[2] = Utility::val<real>(sz);
310  if (direction) {
311  if (!(str >> svx >> svy >> svz))
312  throw GeographicErr("Incomplete input: " + s);
313  v[0] = Utility::val<real>(svx);
314  v[1] = Utility::val<real>(svy);
315  v[2] = Utility::val<real>(svz);
316  }
317  } else {
318  if (!(str >> sbet >> somg))
319  throw GeographicErr("Incomplete input: " + s);
320  ang::DecodeLatLon(sbet, somg, bet, omg, longfirst);
321  if (mode != coord::ELLIPSOIDAL && (bet.n() != 0 || signbit(bet.c())))
322  throw GeographicErr("Latitude outside range [-90,90]: " + s);
323  if (threed) {
324  if (!(str >> sh))
325  throw GeographicErr("Incomplete input: " + s);
326  h = Utility::val<real>(sh);
327  } else if (direction) {
328  if (!(str >> salp))
329  throw GeographicErr("Incomplete input: " + s);
330  alp = ang::DecodeAzimuth(salp);
331  }
332  }
333  if (str >> strc)
334  throw GeographicErr("Extraneous input: " + strc);
335  // PROCESS
336  if (reverse) {
337  if (threed)
338  tc.carttoany(r, mode, bet, omg, h);
339  else if (direction)
340  tc.cart2toany(r, v, mode, bet, omg, alp);
341  else
342  tc.cart2toany(r, mode, bet, omg);
343  } else {
344  if (threed)
345  tc.anytocart(mode, bet, omg, h, r);
346  else if (direction)
347  tc.anytocart2(mode, bet, omg, alp, r, v);
348  else
349  tc.anytocart2(mode, bet, omg, r);
350  }
351  // WRITE
352  if (reverse) {
353  *output << ang::LatLonString(bet, omg,
354  angprec, dms, dmssep, longfirst);
355  if (threed)
356  *output << " " << Utility::str(h, disprec);
357  else if (direction)
358  *output << " " << ang::AzimuthString(alp, angprec, dms, dmssep);
359  } else {
360  *output << Utility::str(r[0], disprec) << " "
361  << Utility::str(r[1], disprec) << " "
362  << Utility::str(r[2], disprec);
363  if (direction)
364  *output << " "
365  << Utility::str(v[0], vecprec) << " "
366  << Utility::str(v[1], vecprec) << " "
367  << Utility::str(v[2], vecprec);
368  }
369  *output << eol;
370  }
371  catch (const std::exception& e) {
372  // Write error message cout so output lines match input lines
373  *output << "ERROR: " << e.what() << "\n";
374  retval = 1;
375  }
376  }
377  return retval;
378  }
379  catch (const std::exception& e) {
380  std::cerr << "Caught exception: " << e.what() << "\n";
381  return 1;
382  }
383  catch (...) {
384  std::cerr << "Caught unknown exception\n";
385  return 1;
386  }
387 }
std::array< Math::real, 3 > vec3
Definition: Ellipsoid3.hpp:88
static void DecodeLatLon(const std::string &stra, const std::string &strb, AngleT &lat, AngleT &lon, bool longfirst=false)
Definition: Angle.cpp:39
Header for GeographicLib::Utility class.
AngleT< Math::real > Angle
Definition: Angle.hpp:760
static int extra_digits()
Definition: Math.cpp:49
int usage(int retval, bool)
Definition: Geod3ODE.cpp:41
static AngleT DecodeAzimuth(const std::string &azistr)
Definition: Angle.cpp:62
Namespace for GeographicLib.
Definition: Accumulator.cpp:12
int main(int argc, const char *const argv[])
static std::string AzimuthString(AngleT azi, int prec, bool dms=false, char dmssep='\0')
Definition: Angle.cpp:86
static std::string str(T x, int p=-1)
Definition: Utility.hpp:161
GeographicLib::Math::real real
Definition: Geod3Solve.cpp:25
static int set_digits(int ndigits=0)
Definition: Utility.cpp:184
Exception handling for GeographicLib.
Definition: Constants.hpp:344
Header for GeographicLib::Triaxial::Cartesian3 class.
static std::string LatLonString(AngleT lat, AngleT lon, int prec, bool dms=false, char dmssep='\0', bool longfirst=false)
Definition: Angle.cpp:72
Header for the GeographicLib::AngleT class.
Header for GeographicLib::DMS class.