GeographicLib 2.7

Loading...
Searching...
No Matches
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>
19#include <GeographicLib/Utility.hpp>
20#include <GeographicLib/Angle.hpp>
21#include <GeographicLib/Triaxial/Geodesic3.hpp>
22
23#include "Geod3Solve.usage"
24
25 using real = GeographicLib::Math::real;
26 using ang = GeographicLib::Angle;
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;
58 Utility::set_digits();
59 bool inverse = false,
60 dms = false, full = false, unroll = false,
61 longfirst = false,
62 linecalc = false;
63 real
64 a = Constants::Triaxial_Earth_a(),
65 b = Constants::Triaxial_Earth_b(),
66 c = Constants::Triaxial_Earth_c(),
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}
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
main
int main(int argc, const char *const argv[])
Definition Geod3Solve.cpp:53
ang
GeographicLib::Angle ang
Definition Geod3Solve.cpp:26
BiaxialCoords
void BiaxialCoords(bool fwd, real f, ang &bet, ang &omg)
Definition Geod3Solve.cpp:28
real
GeographicLib::Math::real real
Definition Geod3Solve.cpp:25
Geodesic3.hpp
Header for GeographicLib::Triaxial::Geodesic3 class.
Math.hpp
Header for GeographicLib::Math class.
Utility.hpp
Header for GeographicLib::Utility class.
GeographicLib::AngleT::base
AngleT base() const
Definition Angle.hpp:642
GeographicLib::AngleT< Math::real >::cardinal
static AngleT cardinal(Math::real q)
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::modang
AngleT modang(T m) const
Definition Angle.hpp:711
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::AngleT::reflect
AngleT & reflect(bool flips, bool flipc=false, bool swapp=false)
Definition Angle.hpp:696
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
Namespace for GeographicLib.
Definition Accumulator.cpp:12
GeographicLib::Angle
AngleT< Math::real > Angle
Definition Angle.hpp:760
std::swap
void swap(GeographicLib::NearestNeighbor< dist_t, pos_t, distfun_t > &a, GeographicLib::NearestNeighbor< dist_t, pos_t, distfun_t > &b)
Definition NearestNeighbor.hpp:819