GeographicLib  2.6
Geod3Solve.cpp
Go to the documentation of this file.
1 /**
2  * \file Geod3Solve.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="Geod3Solve.1.html">man page</a> for usage information.
10  **********************************************************************/
11 
12 #include <iostream>
13 #include <iomanip>
14 #include <string>
15 #include <sstream>
16 #include <fstream>
17 #include <GeographicLib/Math.hpp>
18 #include <GeographicLib/DMS.hpp>
20 #include <GeographicLib/Angle.hpp>
22 
23 #include "Geod3Solve.usage"
24 
27 
28 void BiaxialCoords(bool fwd, real f, ang& bet, ang& omg) {
29  using std::isnan, std::signbit;
30  if (isnan(f)) return;
31  // fwd: biaxial -> triaxial
32  // !fwd: triaxial -> biaxial
33  if (fwd)
34  bet = bet.modang(1 - f);
35  if (signbit(f)) {
36  omg -= ang::cardinal(1);
37  std::swap(bet, omg);
38  omg += ang::cardinal(1);
39  }
40  if (!fwd)
41  bet = bet.modang(1 / (1 - f));
42 }
43 
44 void BiaxialCoords(bool fwd, real f, ang& bet, ang& omg, ang& alp) {
45  using std::isnan, std::signbit;
46  if (isnan(f)) return;
47  // fwd: biaxial -> triaxial
48  // !fwd: triaxial -> biaxial
49  BiaxialCoords(fwd, f, bet, omg);
50  if (signbit(f)) alp.reflect(false, false, true);
51 }
52 
53 int main(int argc, const char* const argv[]) {
54  try {
55  using namespace GeographicLib;
56  using namespace Triaxial;
57  using std::signbit, std::isnan, std::fabs;
59  bool inverse = false,
60  dms = false, full = false, unroll = false,
61  longfirst = false,
62  linecalc = false;
63  real
67  // Markers to determine how the ellipsoid is specified
68  e2 = -1, f = Math::NaN(), k2 = 0, kp2 = 0;
69  ang bet1, omg1, alp1, bet2, omg2, alp2;
70  real s12;
71  int prec = 3;
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 == "-i") {
78  inverse = true;
79  linecalc = false;
80  } else if (arg == "-t") {
81  if (m + 3 >= argc) return usage(1, true);
82  try {
83  a = Utility::val<real>(std::string(argv[m + 1]));
84  b = Utility::val<real>(std::string(argv[m + 2]));
85  c = Utility::val<real>(std::string(argv[m + 3]));
86  }
87  catch (const std::exception& e) {
88  std::cerr << "Error decoding arguments of -t: " << e.what() << "\n";
89  return 1;
90  }
91  f = Math::NaN(); e2 = -1;
92  m += 3;
93  } else if (arg == "-e") {
94  // Cayley ellipsoid sqrt([2,1,1/2]) is
95  // -e 1 3/2 1/3 2/3
96  if (m + 4 >= argc) return usage(1, true);
97  try {
98  b = Utility::val<real>(std::string(argv[m + 1]));
99  e2 = Utility::fract<real>(std::string(argv[m + 2]));
100  k2 = Utility::fract<real>(std::string(argv[m + 3]));
101  kp2 = Utility::fract<real>(std::string(argv[m + 4]));
102  }
103  catch (const std::exception& e) {
104  std::cerr << "Error decoding arguments of -e: " << e.what() << "\n";
105  return 1;
106  }
107  a = -1; f = Math::NaN();
108  m += 4;
109  } else if (arg == "-e2") {
110  if (m + 2 >= argc) return usage(1, true);
111  try {
112  b = Utility::val<real>(std::string(argv[m + 1]));
113  f = Utility::fract<real>(std::string(argv[m + 2]));
114  }
115  // f > 0, k2 = 1, kp2 = 0
116  // a = b; c = b * (1 - f)
117  // e2 = factor(subst([a = A, b = A, c = A*(1-f)],(a^2 - c^2)/b^2))
118  // = f * (2 - f)
119  // f < 0, k2 = 0, kp2 = 1
120  // a = A * (1 - f); c = b
121  // e2 = factor(subst([a = A*(1-f), b = A, c = A],(a^2 - c^2)/b^2))
122  // = -f * (2 - f)
123  catch (const std::exception& e) {
124  std::cerr << "Error decoding arguments of -e2: " << e.what() << "\n";
125  return 1;
126  }
127  a = e2 = -1;
128  m += 2;
129  } else if (arg == "-L") {
130  linecalc = true;
131  inverse = false;
132  if (m + 3 >= argc) return usage(1, true);
133  try {
134  ang::DecodeLatLon(std::string(argv[m + 1]), std::string(argv[m + 2]),
135  bet1, omg1, longfirst);
136  alp1 = ang::DecodeAzimuth(std::string(argv[m + 3]));
137  }
138  catch (const std::exception& e) {
139  std::cerr << "Error decoding arguments of -L: " << e.what() << "\n";
140  return 1;
141  }
142  m += 3;
143  } else if (arg == "-u")
144  unroll = true;
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 == "-f")
154  full = true;
155  else if (arg == "-p") {
156  if (++m == argc) return usage(1, true);
157  try {
158  prec = Utility::val<int>(std::string(argv[m]));
159  }
160  catch (const std::exception&) {
161  std::cerr << "Precision " << argv[m] << " is not a number\n";
162  return 1;
163  }
164  } else if (arg == "--input-string") {
165  if (++m == argc) return usage(1, true);
166  istring = argv[m];
167  } else if (arg == "--input-file") {
168  if (++m == argc) return usage(1, true);
169  ifile = argv[m];
170  } else if (arg == "--output-file") {
171  if (++m == argc) return usage(1, true);
172  ofile = argv[m];
173  } else if (arg == "--line-separator") {
174  if (++m == argc) return usage(1, true);
175  if (std::string(argv[m]).size() != 1) {
176  std::cerr << "Line separator must be a single character\n";
177  return 1;
178  }
179  lsep = argv[m][0];
180  } else if (arg == "--comment-delimiter") {
181  if (++m == argc) return usage(1, true);
182  cdelim = argv[m];
183  } else if (arg == "--version") {
184  std::cout << argv[0] << ": GeographicLib version "
185  << GEOGRAPHICLIB_VERSION_STRING << "\n";
186  return 0;
187  } else
188  return usage(!(arg == "-h" || arg == "--help"), arg != "--help");
189  }
190 
191  Geodesic3 t = e2 >= 0 ? Geodesic3(b, e2, k2, kp2) :
192  !isnan(f) ? Geodesic3(b, fabs(f) * (2 - f),
193  signbit(f) ? 0 : 1, signbit(f) ? 1 : 0) :
194  Geodesic3(a, b, c);
195 
196  if (!ifile.empty() && !istring.empty()) {
197  std::cerr << "Cannot specify --input-string and --input-file together\n";
198  return 1;
199  }
200  if (ifile == "-") ifile.clear();
201  std::ifstream infile;
202  std::istringstream instring;
203  if (!ifile.empty()) {
204  infile.open(ifile.c_str());
205  if (!infile.is_open()) {
206  std::cerr << "Cannot open " << ifile << " for reading\n";
207  return 1;
208  }
209  } else if (!istring.empty()) {
210  std::string::size_type m = 0;
211  while (true) {
212  m = istring.find(lsep, m);
213  if (m == std::string::npos)
214  break;
215  istring[m] = '\n';
216  }
217  instring.str(istring);
218  }
219  std::istream* input = !ifile.empty() ? &infile :
220  (!istring.empty() ? &instring : &std::cin);
221 
222  std::ofstream outfile;
223  if (ofile == "-") ofile.clear();
224  if (!ofile.empty()) {
225  outfile.open(ofile.c_str());
226  if (!outfile.is_open()) {
227  std::cerr << "Cannot open " << ofile << " for writing\n";
228  return 1;
229  }
230  }
231  std::ostream* output = !ofile.empty() ? &outfile : &std::cout;
232 
233  if (linecalc) {
234  BiaxialCoords(true, f, bet1, omg1, alp1);
235  }
236  std::unique_ptr<GeodesicLine3> lp = linecalc ?
237  std::make_unique<GeodesicLine3>(t, bet1, omg1, alp1) : nullptr;
238  // Max precision = 10: 0.1 nm in distance, 10^-15 deg (= 0.11 nm),
239  // 10^-11 sec (= 0.3 nm).
240  prec = std::min(10 + Math::extra_digits(), std::max(0, prec));
241  using std::round, std::log10, std::ceil;
242  int disprec = std::max(0, prec + int(round(log10(6400000/b)))),
243  angprec = prec + 5;
244  std::string s, eol, sbet1, somg1, salp1, sbet2, somg2, salp2, ss12, strc;
245  std::istringstream str;
246  int retval = 0;
247  while (std::getline(*input, s)) {
248  try {
249  eol = "\n";
250  if (!cdelim.empty()) {
251  std::string::size_type m = s.find(cdelim);
252  if (m != std::string::npos) {
253  eol = " " + s.substr(m) + "\n";
254  s = s.substr(0, m);
255  }
256  }
257  str.clear(); str.str(s);
258 
259  if (inverse) {
260  if (!(str >> sbet1 >> somg1 >> sbet2 >> somg2))
261  throw GeographicErr("Incomplete input: " + s);
262  if (str >> strc)
263  throw GeographicErr("Extraneous input: " + strc);
264  ang::DecodeLatLon(sbet1, somg1, bet1, omg1, longfirst);
265  ang::DecodeLatLon(sbet2, somg2, bet2, omg2, longfirst);
266  BiaxialCoords(true, f, bet1, omg1);
267  BiaxialCoords(true, f, bet2, omg2);
268  GeodesicLine3 l = t.Inverse(bet1, omg1, bet2, omg2,
269  s12, alp1, alp2);
270  if (unroll && full) {
271  l.Position(s12, bet2, omg2, alp2);
272  }
273  BiaxialCoords(false, f, bet1, omg1, alp1);
274  BiaxialCoords(false, f, bet2, omg2, alp2);
275  if (full)
276  *output << ang::LatLonString(bet1, omg1,
277  angprec, dms, dmssep, longfirst)
278  << " " << ang::AzimuthString(alp1, angprec, dms, dmssep)
279  << " "
280  << ang::LatLonString(bet2, omg2,
281  angprec, dms, dmssep, longfirst)
282  << " " << ang::AzimuthString(alp2, angprec, dms, dmssep)
283  << " "
284  << Utility::str(s12, disprec) << eol;
285  else
286  *output << ang::AzimuthString(alp1, angprec, dms, dmssep) << " "
287  << ang::AzimuthString(alp2, angprec, dms, dmssep) << " "
288  << Utility::str(s12, disprec) << eol;
289  } else {
290  if (linecalc) {
291  if (!(str >> ss12))
292  throw GeographicErr("Incomplete input: " + s);
293  } else {
294  if (!(str >> sbet1 >> somg1 >> salp1 >> ss12))
295  throw GeographicErr("Incomplete input: " + s);
296  }
297  if (str >> strc)
298  throw GeographicErr("Extraneous input: " + strc);
299  s12 = Utility::val<real>(ss12);
300  if (linecalc)
301  lp->Position(s12, bet2, omg2, alp2);
302  else {
303  ang::DecodeLatLon(sbet1, somg1, bet1, omg1, longfirst);
304  alp1 = ang::DecodeAzimuth(salp1);
305  BiaxialCoords(true, f, bet1, omg1, alp1);
306  t.Direct(bet1, omg1, alp1, s12, bet2, omg2, alp2);
307  }
308  if (!unroll) {
309  Ellipsoid3::AngNorm(bet2, omg2, alp2, !isnan(f) && signbit(f));
310  bet2 = bet2.base();
311  omg2 = omg2.base();
312  alp2 = alp2.base();
313  }
314  BiaxialCoords(false, f, bet1, omg1, alp1);
315  BiaxialCoords(false, f, bet2, omg2, alp2);
316  if (full)
317  *output << ang::LatLonString(bet1, omg1,
318  angprec, dms, dmssep, longfirst)
319  << " " << ang::AzimuthString(alp1, angprec, dms, dmssep)
320  << " "
321  << ang::LatLonString(bet2, omg2,
322  angprec, dms, dmssep, longfirst)
323  << " " << ang::AzimuthString(alp2, angprec, dms, dmssep)
324  << " "
325  << Utility::str(s12, disprec) << eol;
326  else
327  *output << ang::LatLonString(bet2, omg2,
328  angprec, dms, dmssep, longfirst)
329  << " "
330  << ang::AzimuthString(alp2, angprec, dms, dmssep) << eol;
331  }
332  }
333  catch (const std::exception& e) {
334  // Write error message cout so output lines match input lines
335  *output << "ERROR: " << e.what() << "\n";
336  retval = 1;
337  }
338  }
339  return retval;
340  }
341  catch (const std::exception& e) {
342  std::cerr << "Caught exception: " << e.what() << "\n";
343  return 1;
344  }
345  catch (...) {
346  std::cerr << "Caught unknown exception\n";
347  return 1;
348  }
349 }
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 & reflect(bool flips, bool flipc=false, bool swapp=false)
Definition: Angle.hpp:696
AngleT< Math::real > Angle
Definition: Angle.hpp:760
static AngleT cardinal(Math::real q)
Definition: Angle.hpp:721
static int extra_digits()
Definition: Math.cpp:49
int usage(int retval, bool)
Definition: Geod3ODE.cpp:41
Header for GeographicLib::Triaxial::Geodesic3 class.
Header for GeographicLib::Math class.
static AngleT DecodeAzimuth(const std::string &azistr)
Definition: Angle.cpp:62
static bool AngNorm(Angle &bet, Angle &omg, Angle &alp, bool alt=false)
Definition: Ellipsoid3.hpp:247
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
void BiaxialCoords(bool fwd, real f, ang &bet, ang &omg)
Definition: Geod3Solve.cpp:28
GeographicLib::Math::real real
Definition: Geod3Solve.cpp:25
static int set_digits(int ndigits=0)
Definition: Utility.cpp:184
void swap(GeographicLib::NearestNeighbor< dist_t, pos_t, distfun_t > &a, GeographicLib::NearestNeighbor< dist_t, pos_t, distfun_t > &b)
Exception handling for GeographicLib.
Definition: Constants.hpp:344
AngleT modang(T m) const
Definition: Angle.hpp:711
static std::string LatLonString(AngleT lat, AngleT lon, int prec, bool dms=false, char dmssep='\0', bool longfirst=false)
Definition: Angle.cpp:72
AngleT base() const
Definition: Angle.hpp:642
Header for the GeographicLib::AngleT class.
int main(int argc, const char *const argv[])
Definition: Geod3Solve.cpp:53
Header for GeographicLib::DMS class.