GeographicLib 2.7

Loading...
Searching...
No Matches
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>
20#include <GeographicLib/Utility.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
32 using real = GeographicLib::Math::real;
33 using ang = GeographicLib::Angle;
34
35std::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 be 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;
99 Utility::set_digits();
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
104 a = Constants::Triaxial_Earth_a(),
105 b = Constants::Triaxial_Earth_b(),
106 c = Constants::Triaxial_Earth_c(),
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}
Angle.hpp
Header for the GeographicLib::AngleT class.
DMS.hpp
Header for GeographicLib::DMS class.
usage
int usage(int retval, bool)
Definition Geod3ODE.cpp:41
ErrorString
std::string ErrorString(real err, int prec)
Definition Geod3ODE.cpp:35
main
int main(int argc, const char *const argv[])
Definition Geod3ODE.cpp:94
ang
GeographicLib::Angle ang
Definition Geod3Solve.cpp:26
real
GeographicLib::Math::real real
Definition Geod3Solve.cpp:25
Math.hpp
Header for GeographicLib::Math class.
TriaxialGeodesicODE.hpp
Header for GeographicLib::Triaxial class.
Utility.hpp
Header for GeographicLib::Utility class.
GeographicLib::AngleT< Math::real >::DecodeLatLon
static void DecodeLatLon(const std::string &stra, const std::string &strb, AngleT &lat, AngleT &lon, bool longfirst=false)
GeographicLib::AngleT< Math::real >::LatLonString
static std::string LatLonString(AngleT lat, AngleT lon, int prec, bool dms=false, char dmssep='\0', bool longfirst=false)
GeographicLib::AngleT< Math::real >::DecodeAzimuth
static AngleT DecodeAzimuth(const std::string &azistr)
GeographicLib::AngleT< Math::real >::AzimuthString
static std::string AzimuthString(AngleT azi, int prec, bool dms=false, char dmssep='\0')
GeographicLib::Constants::Triaxial_Earth_b
static T Triaxial_Earth_b()
Definition Constants.hpp:218
GeographicLib::Constants::Triaxial_Earth_a
static T Triaxial_Earth_a()
Definition Constants.hpp:211
GeographicLib::Constants::Triaxial_Earth_c
static T Triaxial_Earth_c()
Definition Constants.hpp:225
GeographicLib::GeographicErr
Exception handling for GeographicLib.
Definition Constants.hpp:344
GeographicLib::Math::NaN
static T NaN()
Definition Math.cpp:301
GeographicLib::Math::real
double real
Definition Math.hpp:115
GeographicLib::Math::extra_digits
static int extra_digits()
Definition Math.cpp:49
GeographicLib::Utility::fract
static T fract(const std::string &s)
Definition Utility.hpp:298
GeographicLib::Utility::val
static T val(const std::string &s)
Definition Utility.hpp:225
GeographicLib::Utility::set_digits
static int set_digits(int ndigits=0)
Definition Utility.cpp:184
GeographicLib::Utility::str
static std::string str(T x, int p=-1)
Definition Utility.hpp:161
GeographicLib::Triaxial
Namespace for operations on triaxial ellipsoids.
Definition Cartesian3.cpp:13
GeographicLib::experimental
Namespace for experimental components of GeographicLib.
Definition TriaxialGeodesicODE.hpp:66
GeographicLib
Namespace for GeographicLib.
Definition Accumulator.cpp:12
GeographicLib::Angle
AngleT< Math::real > Angle
Definition Angle.hpp:760