GeographicLib 2.3
GeodSolve.cpp
Go to the documentation of this file.
1/**
2 * \file GeodSolve.cpp
3 * \brief Command line utility for geodesic calculations
4 *
5 * Copyright (c) Charles Karney (2009-2023) <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="GeodSolve.1.html">man page</a> for usage information.
10 **********************************************************************/
11
12#include <iostream>
13#include <string>
14#include <sstream>
15#include <fstream>
18#include <GeographicLib/DMS.hpp>
20
21#if defined(_MSC_VER)
22// Squelch warnings about constant conditional expressions and potentially
23// uninitialized local variables
24# pragma warning (disable: 4127 4701)
25#endif
26
27#include "GeodSolve.usage"
28
30
31std::string LatLonString(real lat, real lon, int prec, bool dms, char dmssep,
32 bool longfirst) {
33 using namespace GeographicLib;
34 std::string
35 latstr = dms ? DMS::Encode(lat, prec + 5, DMS::LATITUDE, dmssep) :
36 DMS::Encode(lat, prec + 5, DMS::NUMBER),
37 lonstr = dms ? DMS::Encode(lon, prec + 5, DMS::LONGITUDE, dmssep) :
38 DMS::Encode(lon, prec + 5, DMS::NUMBER);
39 return
40 (longfirst ? lonstr : latstr) + " " + (longfirst ? latstr : lonstr);
41}
42
43std::string AzimuthString(real azi, int prec, bool dms, char dmssep) {
44 using namespace GeographicLib;
45 return dms ? DMS::Encode(azi, prec + 5, DMS::AZIMUTH, dmssep) :
46 DMS::Encode(azi, prec + 5, DMS::NUMBER);
47}
48
49std::string DistanceStrings(real s12, real a12,
50 bool full, bool arcmode, int prec, bool dms) {
51 using namespace GeographicLib;
52 std::string s;
53 if (full || !arcmode)
54 s += Utility::str(s12, prec);
55 if (full)
56 s += " ";
57 if (full || arcmode)
58 s += DMS::Encode(a12, prec + 5, dms ? DMS::NONE : DMS::NUMBER);
59 return s;
60}
61
62real ReadDistance(const std::string& s, bool arcmode, bool fraction = false) {
63 using namespace GeographicLib;
64 return fraction ? Utility::fract<real>(s) :
65 (arcmode ? DMS::DecodeAngle(s) : Utility::val<real>(s));
66}
67
68int main(int argc, const char* const argv[]) {
69 try {
70 using namespace GeographicLib;
71 enum { NONE = 0, LINE, DIRECT, INVERSE };
72 Utility::set_digits();
73 bool inverse = false, arcmode = false,
74 dms = false, full = false, exact = false, unroll = false,
75 longfirst = false, azi2back = false, fraction = false,
76 arcmodeline = false;
77 real
78 a = Constants::WGS84_a(),
79 f = Constants::WGS84_f();
80 real lat1, lon1, azi1, lat2, lon2, azi2, s12, m12, a12, M12, M21, S12,
81 mult = 1;
82 int linecalc = NONE, prec = 3;
83 std::string istring, ifile, ofile, cdelim;
84 char lsep = ';', dmssep = char(0);
85
86 for (int m = 1; m < argc; ++m) {
87 std::string arg(argv[m]);
88 if (arg == "-i") {
89 inverse = true;
90 linecalc = NONE;
91 } else if (arg == "-a")
92 arcmode = !arcmode;
93 else if (arg == "-F")
94 fraction = true;
95 else if (arg == "-L") {
96 inverse = false;
97 linecalc = LINE;
98 if (m + 3 >= argc) return usage(1, true);
99 try {
100 DMS::DecodeLatLon(std::string(argv[m + 1]), std::string(argv[m + 2]),
101 lat1, lon1, longfirst);
102 azi1 = DMS::DecodeAzimuth(std::string(argv[m + 3]));
103 }
104 catch (const std::exception& e) {
105 std::cerr << "Error decoding arguments of -L: " << e.what() << "\n";
106 return 1;
107 }
108 m += 3;
109 } else if (arg == "-D") {
110 inverse = false;
111 linecalc = DIRECT;
112 if (m + 4 >= argc) return usage(1, true);
113 try {
114 DMS::DecodeLatLon(std::string(argv[m + 1]), std::string(argv[m + 2]),
115 lat1, lon1, longfirst);
116 azi1 = DMS::DecodeAzimuth(std::string(argv[m + 3]));
117 s12 = ReadDistance(std::string(argv[m + 4]), arcmode);
118 arcmodeline = arcmode;
119 }
120 catch (const std::exception& e) {
121 std::cerr << "Error decoding arguments of -D: " << e.what() << "\n";
122 return 1;
123 }
124 m += 4;
125 } else if (arg == "-I") {
126 inverse = false;
127 linecalc = INVERSE;
128 if (m + 4 >= argc) return usage(1, true);
129 try {
130 DMS::DecodeLatLon(std::string(argv[m + 1]), std::string(argv[m + 2]),
131 lat1, lon1, longfirst);
132 DMS::DecodeLatLon(std::string(argv[m + 3]), std::string(argv[m + 4]),
133 lat2, lon2, longfirst);
134 }
135 catch (const std::exception& e) {
136 std::cerr << "Error decoding arguments of -I: " << e.what() << "\n";
137 return 1;
138 }
139 m += 4;
140 } else if (arg == "-e") {
141 if (m + 2 >= argc) return usage(1, true);
142 try {
143 a = Utility::val<real>(std::string(argv[m + 1]));
144 f = Utility::fract<real>(std::string(argv[m + 2]));
145 }
146 catch (const std::exception& e) {
147 std::cerr << "Error decoding arguments of -e: " << e.what() << "\n";
148 return 1;
149 }
150 m += 2;
151 } else if (arg == "-u")
152 unroll = true;
153 else if (arg == "-d") {
154 dms = true;
155 dmssep = '\0';
156 } else if (arg == "-:") {
157 dms = true;
158 dmssep = ':';
159 } else if (arg == "-w")
160 longfirst = !longfirst;
161 else if (arg == "-b")
162 azi2back = true;
163 else if (arg == "-f")
164 full = true;
165 else if (arg == "-p") {
166 if (++m == argc) return usage(1, true);
167 try {
168 prec = Utility::val<int>(std::string(argv[m]));
169 }
170 catch (const std::exception&) {
171 std::cerr << "Precision " << argv[m] << " is not a number\n";
172 return 1;
173 }
174 } else if (arg == "-E")
175 exact = true;
176 else if (arg == "--input-string") {
177 if (++m == argc) return usage(1, true);
178 istring = argv[m];
179 } else if (arg == "--input-file") {
180 if (++m == argc) return usage(1, true);
181 ifile = argv[m];
182 } else if (arg == "--output-file") {
183 if (++m == argc) return usage(1, true);
184 ofile = argv[m];
185 } else if (arg == "--line-separator") {
186 if (++m == argc) return usage(1, true);
187 if (std::string(argv[m]).size() != 1) {
188 std::cerr << "Line separator must be a single character\n";
189 return 1;
190 }
191 lsep = argv[m][0];
192 } else if (arg == "--comment-delimiter") {
193 if (++m == argc) return usage(1, true);
194 cdelim = argv[m];
195 } else if (arg == "--version") {
196 std::cout << argv[0] << ": GeographicLib version "
197 << GEOGRAPHICLIB_VERSION_STRING << "\n";
198 return 0;
199 } else
200 return usage(!(arg == "-h" || arg == "--help"), arg != "--help");
201 }
202
203 if (!ifile.empty() && !istring.empty()) {
204 std::cerr << "Cannot specify --input-string and --input-file together\n";
205 return 1;
206 }
207 if (ifile == "-") ifile.clear();
208 std::ifstream infile;
209 std::istringstream instring;
210 if (!ifile.empty()) {
211 infile.open(ifile.c_str());
212 if (!infile.is_open()) {
213 std::cerr << "Cannot open " << ifile << " for reading\n";
214 return 1;
215 }
216 } else if (!istring.empty()) {
217 std::string::size_type m = 0;
218 while (true) {
219 m = istring.find(lsep, m);
220 if (m == std::string::npos)
221 break;
222 istring[m] = '\n';
223 }
224 instring.str(istring);
225 }
226 std::istream* input = !ifile.empty() ? &infile :
227 (!istring.empty() ? &instring : &std::cin);
228
229 std::ofstream outfile;
230 if (ofile == "-") ofile.clear();
231 if (!ofile.empty()) {
232 outfile.open(ofile.c_str());
233 if (!outfile.is_open()) {
234 std::cerr << "Cannot open " << ofile << " for writing\n";
235 return 1;
236 }
237 }
238 std::ostream* output = !ofile.empty() ? &outfile : &std::cout;
239
240 unsigned outmask = Geodesic::LATITUDE | Geodesic::LONGITUDE |
241 Geodesic::AZIMUTH; // basic output quantities
242 outmask |= inverse ? Geodesic::DISTANCE : // distance-related flags
243 (arcmode ? Geodesic::NONE : Geodesic::DISTANCE_IN);
244 // longitude unrolling
245 outmask |= unroll ? Geodesic::LONG_UNROLL : Geodesic::NONE;
246 // full output -- don't use Geodesic::ALL since this includes DISTANCE_IN
247 outmask |= full ? (Geodesic::DISTANCE | Geodesic::REDUCEDLENGTH |
248 Geodesic::GEODESICSCALE | Geodesic::AREA) :
249 Geodesic::NONE;
250
251 const Geodesic geods(a, f, exact);
252 GeodesicLine ls;
253 if (linecalc) {
254 if (linecalc == LINE) fraction = false;
255 ls = linecalc == DIRECT ?
256 geods.GenDirectLine(lat1, lon1, azi1, arcmodeline, s12, outmask) :
257 linecalc == INVERSE ?
258 geods.InverseLine(lat1, lon1, lat2, lon2, outmask) :
259 // linecalc == LINE
260 geods.Line(lat1, lon1, azi1, outmask);
261 mult = fraction ? ls.GenDistance(arcmode) : 1;
262 if (linecalc == INVERSE) azi1 = ls.Azimuth();
263 }
264
265 // Max precision = 10: 0.1 nm in distance, 10^-15 deg (= 0.11 nm),
266 // 10^-11 sec (= 0.3 nm).
267 prec = std::min(10 + Math::extra_digits(), std::max(0, prec));
268 std::string s, eol, slat1, slon1, slat2, slon2, sazi1, ss12, strc;
269 std::istringstream str;
270 int retval = 0;
271 while (std::getline(*input, s)) {
272 try {
273 eol = "\n";
274 if (!cdelim.empty()) {
275 std::string::size_type m = s.find(cdelim);
276 if (m != std::string::npos) {
277 eol = " " + s.substr(m) + "\n";
278 s = s.substr(0, m);
279 }
280 }
281 str.clear(); str.str(s);
282 if (inverse) {
283 if (!(str >> slat1 >> slon1 >> slat2 >> slon2))
284 throw GeographicErr("Incomplete input: " + s);
285 if (str >> strc)
286 throw GeographicErr("Extraneous input: " + strc);
287 DMS::DecodeLatLon(slat1, slon1, lat1, lon1, longfirst);
288 DMS::DecodeLatLon(slat2, slon2, lat2, lon2, longfirst);
289 a12 = geods.GenInverse(lat1, lon1, lat2, lon2, outmask,
290 s12, azi1, azi2, m12, M12, M21, S12);
291 if (full) {
292 if (unroll) {
293 real e;
294 lon2 = lon1 + Math::AngDiff(lon1, lon2, e);
295 lon2 += e;
296 } else {
297 lon1 = Math::AngNormalize(lon1);
298 lon2 = Math::AngNormalize(lon2);
299 }
300 *output << LatLonString(lat1, lon1, prec, dms, dmssep, longfirst)
301 << " ";
302 }
303 *output << AzimuthString(azi1, prec, dms, dmssep) << " ";
304 if (full)
305 *output << LatLonString(lat2, lon2, prec, dms, dmssep, longfirst)
306 << " ";
307 if (azi2back) {
308 using std::copysign;
309 // map +/-0 -> -/+180; +/-180 -> -/+0
310 // this depends on abs(azi2) <= 180
311 azi2 = copysign(azi2 + copysign(real(Math::hd), -azi2), -azi2);
312 }
313 *output << AzimuthString(azi2, prec, dms, dmssep) << " "
314 << DistanceStrings(s12, a12, full, arcmode, prec, dms);
315 if (full)
316 *output << " " << Utility::str(m12, prec)
317 << " " << Utility::str(M12, prec+7)
318 << " " << Utility::str(M21, prec+7)
319 << " " << Utility::str(S12, std::max(prec-7, 0));
320 *output << eol;
321 } else {
322 if (linecalc) {
323 if (!(str >> ss12))
324 throw GeographicErr("Incomplete input: " + s);
325 if (str >> strc)
326 throw GeographicErr("Extraneous input: " + strc);
327 // In fraction mode input is read as a distance
328 s12 = ReadDistance(ss12, !fraction && arcmode, fraction) * mult;
329 a12 = ls.GenPosition(arcmode, s12, outmask,
330 lat2, lon2, azi2, s12, m12, M12, M21, S12);
331 } else {
332 if (!(str >> slat1 >> slon1 >> sazi1 >> ss12))
333 throw GeographicErr("Incomplete input: " + s);
334 if (str >> strc)
335 throw GeographicErr("Extraneous input: " + strc);
336 DMS::DecodeLatLon(slat1, slon1, lat1, lon1, longfirst);
337 azi1 = DMS::DecodeAzimuth(sazi1);
338 s12 = ReadDistance(ss12, arcmode);
339 a12 = geods.GenDirect(lat1, lon1, azi1, arcmode, s12, outmask,
340 lat2, lon2, azi2, s12, m12, M12, M21, S12);
341 }
342 if (full)
343 *output
344 << LatLonString(lat1, unroll ? lon1 : Math::AngNormalize(lon1),
345 prec, dms, dmssep, longfirst)
346 << " " << AzimuthString(azi1, prec, dms, dmssep) << " ";
347 if (azi2back) {
348 using std::copysign;
349 // map +/-0 -> -/+180; +/-180 -> -/+0
350 // this depends on abs(azi2) <= 180
351 azi2 = copysign(azi2 + copysign(real(Math::hd), -azi2), -azi2);
352 }
353 *output << LatLonString(lat2, lon2, prec, dms, dmssep, longfirst)
354 << " " << AzimuthString(azi2, prec, dms, dmssep);
355 if (full)
356 *output << " "
357 << DistanceStrings(s12, a12, full, arcmode, prec, dms)
358 << " " << Utility::str(m12, prec)
359 << " " << Utility::str(M12, prec+7)
360 << " " << Utility::str(M21, prec+7)
361 << " " << Utility::str(S12, std::max(prec-7, 0));
362 *output << eol;
363 }
364 }
365 catch (const std::exception& e) {
366 // Write error message cout so output lines match input lines
367 *output << "ERROR: " << e.what() << "\n";
368 retval = 1;
369 }
370 }
371 return retval;
372 }
373 catch (const std::exception& e) {
374 std::cerr << "Caught exception: " << e.what() << "\n";
375 return 1;
376 }
377 catch (...) {
378 std::cerr << "Caught unknown exception\n";
379 return 1;
380 }
381}
Header for GeographicLib::DMS class.
GeographicLib::Math::real real
Definition: GeodSolve.cpp:29
std::string DistanceStrings(real s12, real a12, bool full, bool arcmode, int prec, bool dms)
Definition: GeodSolve.cpp:49
int main(int argc, const char *const argv[])
Definition: GeodSolve.cpp:68
real ReadDistance(const std::string &s, bool arcmode, bool fraction=false)
Definition: GeodSolve.cpp:62
std::string AzimuthString(real azi, int prec, bool dms, char dmssep)
Definition: GeodSolve.cpp:43
std::string LatLonString(real lat, real lon, int prec, bool dms, char dmssep, bool longfirst)
Definition: GeodSolve.cpp:31
Header for GeographicLib::GeodesicLine class.
Header for GeographicLib::Geodesic class.
Header for GeographicLib::Utility class.
Math::real Azimuth() const
Math::real GenDistance(bool arcmode) const
Math::real GenPosition(bool arcmode, real s12_a12, unsigned outmask, real &lat2, real &lon2, real &azi2, real &s12, real &m12, real &M12, real &M21, real &S12) const
Geodesic calculations
Definition: Geodesic.hpp:175
Exception handling for GeographicLib.
Definition: Constants.hpp:316
Namespace for GeographicLib.
Definition: Accumulator.cpp:12