GeographicLib  2.6
Geod3ODE.cpp
Go to the documentation of this file.
1 /**
2  * \file Geod3ODE.cpp
3  * \brief Command line utility for using an ODE solver for geodesics on a
4  * triaxial ellipsoid
5  *
6  * Copyright (c) Charles Karney (2024-2025) <karney@alum.mit.edu> and licensed
7  * under the MIT/X11 License. For more information, see
8  * https://geographiclib.sourceforge.io/
9  *
10  * Use "Geod3ODE --help" for brief documentation.
11  **********************************************************************/
12 
13 #include <iostream>
14 #include <iomanip>
15 #include <string>
16 #include <sstream>
17 #include <fstream>
18 #include <GeographicLib/Math.hpp>
19 #include <GeographicLib/DMS.hpp>
21 #include <GeographicLib/Angle.hpp>
22 
23 #if defined(_MSC_VER)
24 // Squelch warning triggered by boost:
25 // 4127: conditional expression is constant
26 # pragma warning (disable: 4127)
27 #endif
28 #include "TriaxialGeodesicODE.hpp"
29 
30 // #include "GeodSolve.usage"
31 
34 
35 std::string ErrorString(real err, int prec) {
36  std::ostringstream s;
37  s << std::scientific << std::setprecision(prec) << err;
38  return s.str();
39 }
40 
41 int usage(int retval, bool /*brief*/) {
42  (retval ? std::cerr : std:: cout ) << "Usage:\n"
43 "\n"
44 " This implements some of the functionality of Geod3Solve(1) by integrating the\n"
45 " ordinary differential equations for the geodesic. Only direct geodesic\n"
46 " calculations are supported.\n"
47 "\n"
48 " The following options of Geod3Solve(1) are supported\n"
49 " -t a b c | -e b e2 k2 kp2 \n"
50 " -L bet1 omg1 alp1\n"
51 " -u\n"
52 " -d | -:\n"
53 " -w \n"
54 " -f\n"
55 " -p prec\n"
56 "\n"
57 " The following options of Geod3Solve(1) are not supported\n"
58 " -i\n"
59 " -e2\n"
60 " -u\n"
61 "\n"
62 " The following are new options\n"
63 "\n"
64 " -b\n"
65 " bufferd mode (only useful with the -L option). Causes all the s12 values\n"
66 " to be buffered and fed into the integrator at the end. This sorts the\n"
67 " entries so that the integrator doesn't have to the continually restarted.\n"
68 "\n"
69 " -x\n"
70 " extended mode. Computes and prints the reduced length, m12, and the\n"
71 " geodesic scales, M12, M21.\n"
72 "\n"
73 " --eps eps\n"
74 " sets the eps parameter in the constructor for TriaxialGeodesicODE.\n"
75 "\n"
76 " --dense\n"
77 " use the dense solver allowing interpolated way points to tbe computed\n"
78 " inexpensively.\n"
79 "\n"
80 " --normp\n"
81 " force the solution vector onto the ellipsoid when computing the\n"
82 " acceleration.\n"
83 "\n"
84 " --errors\n"
85 " print error estimates, the distance from the ellipsoid (in meters) and\n"
86 " the deviation of the velocity from a unit tangential vector.\n"
87 "\n"
88 " --steps\n"
89 " print the number of integration steps and the number of times the\n"
90 " acceleration was computed.\n";
91  return retval;
92 }
93 
94 int main(int argc, const char* const argv[]) {
95  try {
96  using namespace GeographicLib;
97  using namespace Triaxial;
98  using namespace experimental;
100  bool dms = false, longfirst = false,
101  linecalc = false, extended = false, dense = false, normp = false,
102  buffered = false, full = false, errors = false, steps = false;
103  real
107  e2 = -1, k2 = 0, kp2 = 0, eps = 0;
108  ang bet1 = ang(0), omg1 = ang(0), alp1 = ang(0), bet2, omg2, alp2;
109  real s12, m12, M12, M21;
110  std::vector<real> s12v;
111  int prec = 3;
112  std::string istring, ifile, ofile, cdelim;
113  char lsep = ';', dmssep = char(0);
114 
115  for (int m = 1; m < argc; ++m) {
116  std::string arg(argv[m]);
117  if (arg == "-t") {
118  if (m + 3 >= argc) return usage(1, true);
119  try {
120  a = Utility::val<real>(std::string(argv[m + 1]));
121  b = Utility::val<real>(std::string(argv[m + 2]));
122  c = Utility::val<real>(std::string(argv[m + 3]));
123  }
124  catch (const std::exception& e) {
125  std::cerr << "Error decoding arguments of -t: " << e.what() << "\n";
126  return 1;
127  }
128  e2 = -1;
129  m += 3;
130  } else if (arg == "-e") {
131  // Cayley ellipsoid sqrt([2,1,1/2]) is
132  // -e 1 3/2 1 2
133  if (m + 4 >= argc) return usage(1, true);
134  try {
135  b = Utility::val<real>(std::string(argv[m + 1]));
136  e2 = Utility::fract<real>(std::string(argv[m + 2]));
137  k2 = Utility::fract<real>(std::string(argv[m + 3]));
138  kp2 = Utility::fract<real>(std::string(argv[m + 4]));
139  }
140  catch (const std::exception& e) {
141  std::cerr << "Error decoding arguments of -e: " << e.what() << "\n";
142  return 1;
143  }
144  a = -1;
145  m += 4;
146  } else if (arg == "-x")
147  extended = true;
148  else if (arg == "-L") {
149  linecalc = true;
150  if (m + 3 >= argc) return usage(1, true);
151  try {
152  ang::DecodeLatLon(std::string(argv[m + 1]),
153  std::string(argv[m + 2]),
154  bet1, omg1, longfirst);
155  alp1 = ang::DecodeAzimuth(std::string(argv[m + 3]));
156  }
157  catch (const std::exception& e) {
158  std::cerr << "Error decoding arguments of -L: " << e.what() << "\n";
159  return 1;
160  }
161  m += 3;
162  } else if (arg == "--eps") {
163  if (m + 1 >= argc) return usage(1, true);
164  try {
165  using std::pow;
166  eps = pow(std::numeric_limits<real>::epsilon(),
167  Utility::fract<real>(std::string(argv[m + 1])));
168  }
169  catch (const std::exception& e) {
170  std::cerr << "Error decoding argument of --eps: " << e.what() << "\n";
171  return 1;
172  }
173  m += 1;
174  } else if (arg == "--dense")
175  dense = true;
176  else if (arg == "--normp")
177  normp = true;
178  else if (arg == "--errors")
179  errors = true;
180  else if (arg == "--steps")
181  steps = true;
182  else if (arg == "-b")
183  buffered = true;
184  else if (arg == "-f")
185  full = true;
186  else if (arg == "-d") {
187  dms = true;
188  dmssep = '\0';
189  } else if (arg == "-:") {
190  dms = true;
191  dmssep = ':';
192  } else if (arg == "-w")
193  longfirst = !longfirst;
194  else if (arg == "-p") {
195  if (++m == argc) return usage(1, true);
196  try {
197  prec = Utility::val<int>(std::string(argv[m]));
198  }
199  catch (const std::exception&) {
200  std::cerr << "Precision " << argv[m] << " is not a number\n";
201  return 1;
202  }
203  } else if (arg == "--input-string") {
204  if (++m == argc) return usage(1, true);
205  istring = argv[m];
206  } else if (arg == "--input-file") {
207  if (++m == argc) return usage(1, true);
208  ifile = argv[m];
209  } else if (arg == "--output-file") {
210  if (++m == argc) return usage(1, true);
211  ofile = argv[m];
212  } else if (arg == "--line-separator") {
213  if (++m == argc) return usage(1, true);
214  if (std::string(argv[m]).size() != 1) {
215  std::cerr << "Line separator must be a single character\n";
216  return 1;
217  }
218  lsep = argv[m][0];
219  } else if (arg == "--comment-delimiter") {
220  if (++m == argc) return usage(1, true);
221  cdelim = argv[m];
222  } else if (arg == "--version") {
223  std::cout << argv[0] << ": GeographicLib version "
224  << GEOGRAPHICLIB_VERSION_STRING << "\n";
225  return 0;
226  } else
227  return usage(!(arg == "-h" || arg == "--help"), arg != "--help");
228  }
229 
230  Ellipsoid3 t(e2 >= 0 ? Ellipsoid3(b, e2, k2, kp2) : Ellipsoid3(a, b, c));
231  if (!ifile.empty() && !istring.empty()) {
232  std::cerr << "Cannot specify --input-string and --input-file together\n";
233  return 1;
234  }
235  if (ifile == "-") ifile.clear();
236  std::ifstream infile;
237  std::istringstream instring;
238  if (!ifile.empty()) {
239  infile.open(ifile.c_str());
240  if (!infile.is_open()) {
241  std::cerr << "Cannot open " << ifile << " for reading\n";
242  return 1;
243  }
244  } else if (!istring.empty()) {
245  std::string::size_type m = 0;
246  while (true) {
247  m = istring.find(lsep, m);
248  if (m == std::string::npos)
249  break;
250  istring[m] = '\n';
251  }
252  instring.str(istring);
253  }
254  std::istream* input = !ifile.empty() ? &infile :
255  (!istring.empty() ? &instring : &std::cin);
256 
257  std::ofstream outfile;
258  if (ofile == "-") ofile.clear();
259  if (!ofile.empty()) {
260  outfile.open(ofile.c_str());
261  if (!outfile.is_open()) {
262  std::cerr << "Cannot open " << ofile << " for writing\n";
263  return 1;
264  }
265  }
266  std::ostream* output = !ofile.empty() ? &outfile : &std::cout;
267 
268  using std::round, std::log10;
269  int disprec = int(round(log10(6400000/b)));
270  // Max precision = 10: 0.1 nm in distance, 10^-15 deg (= 0.11 nm),
271  // 10^-11 sec (= 0.3 nm).
272  prec = std::min(10 + Math::extra_digits(), std::max(0, prec));
273  std::string s, eol, sbet1, somg1, salp1, sbet2, somg2, salp2, ss12, strc;
274  std::istringstream str;
275  int retval = 0;
276  buffered = buffered && linecalc;
277  errors = errors && !buffered;
278  TriaxialGeodesicODE l = linecalc ?
279  TriaxialGeodesicODE(t, bet1, omg1, alp1, extended, dense, normp, eps) :
280  TriaxialGeodesicODE(t, extended, dense, normp, eps);
281 
282  while (std::getline(*input, s)) {
283  try {
284  eol = "\n";
285  if (!cdelim.empty()) {
286  std::string::size_type m = s.find(cdelim);
287  if (m != std::string::npos) {
288  eol = " " + s.substr(m) + "\n";
289  s = s.substr(0, m);
290  }
291  }
292  str.clear(); str.str(s);
293  if (!(linecalc ? (str >> ss12) :
294  (str >> sbet1 >> somg1 >> salp1 >> ss12)))
295  throw GeographicErr("Incomplete input: " + s);
296  if (str >> strc)
297  throw GeographicErr("Extraneious input: " + s);
298  s12 = Utility::val<real>(ss12);
299  if (linecalc) {
300  if (buffered) s12v.push_back(s12);
301  } else {
302  ang::DecodeLatLon(sbet1, somg1, bet1, omg1, longfirst);
303  alp1 = ang::DecodeAzimuth(salp1);
304  l.Reset(bet1, omg1, alp1);
305  }
306  if (!buffered) {
307  auto errs = l.Position(s12, bet2, omg2, alp2, m12, M12, M21);
308  if (full)
309  *output << ang::LatLonString(bet1, omg1, prec, dms, dmssep,
310  longfirst) << " "
311  << ang::AzimuthString(alp1, prec, dms, dmssep) << " ";
312  *output << ang::LatLonString(bet2, omg2, prec, dms, dmssep,
313  longfirst) << " "
314  << ang::AzimuthString(alp2, prec, dms, dmssep);
315  if (full)
316  *output << " " << Utility::str(s12, prec + disprec);
317  if (extended)
318  *output << " " << Utility::str(m12, prec + disprec)
319  << " " << Utility::str(M12, prec+7)
320  << " " << Utility::str(M21, prec+7);
321  if (steps)
322  *output << " " << l.NSteps() << " " << l.IntSteps();
323  if (errors)
324  *output << " " << ErrorString(errs.first, 2)
325  << " " << ErrorString(errs.second, 2);
326  *output << eol;
327  }
328  }
329  catch (const std::exception& e) {
330  if (buffered)
331  s12v.push_back(Math::NaN());
332  else
333  // Write error message cout so output lines match input lines
334  *output << "ERROR: " << e.what() << " " << s << "\n";
335  retval = 1;
336  }
337  }
338 
339  if (buffered) {
340  std::vector<ang> bet2v, omg2v, alp2v;
341  std::vector<real> m12v, M12v, M21v;
342  l.Position(s12v, bet2v, omg2v, alp2v, m12v, M12v, M21v);
343  for (size_t i = 0; i < s12v.size(); ++i) {
344  if (full)
345  *output << ang::LatLonString(bet1, omg1, prec, dms, dmssep,
346  longfirst) << " "
347  << ang::AzimuthString(alp1, prec, dms, dmssep) << " ";
348  *output << ang::LatLonString(bet2v[i], omg2v[i], prec, dms, dmssep,
349  longfirst) << " "
350  << ang::AzimuthString(alp2v[i], prec, dms, dmssep);
351  if (full)
352  *output << " " << Utility::str(s12v[i], prec + disprec);
353  if (extended)
354  *output << " " << Utility::str(m12v[i], prec + disprec)
355  << " " << Utility::str(M12v[i], prec+7)
356  << " " << Utility::str(M21v[i], prec+7);
357  *output << eol;
358  }
359  }
360  return retval;
361  }
362  catch (const std::exception& e) {
363  std::cerr << "Caught exception: " << e.what() << "\n";
364  return 1;
365  }
366  catch (...) {
367  std::cerr << "Caught unknown exception\n";
368  return 1;
369  }
370 }
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
Header for GeographicLib::Triaxial class.
static int extra_digits()
Definition: Math.cpp:49
int usage(int retval, bool)
Definition: Geod3ODE.cpp:41
Header for GeographicLib::Math class.
static AngleT DecodeAzimuth(const std::string &azistr)
Definition: Angle.cpp:62
Namespace for GeographicLib.
Definition: Accumulator.cpp:12
static T NaN()
Definition: Math.cpp:301
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
int main(int argc, const char *const argv[])
Definition: Geod3ODE.cpp:94
std::string ErrorString(real err, int prec)
Definition: Geod3ODE.cpp:35
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
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.
GeographicLib::Angle ang
Definition: Geod3ODE.cpp:33
Header for GeographicLib::DMS class.