GeographicLib  2.6
IntersectTool.cpp
Go to the documentation of this file.
1 /**
2  * \file IntersectTool.cpp
3  * \brief Command line utility for geodesic intersections
4  *
5  * Copyright (c) Charles Karney (2023) <karney@alum.mit.edu> and licensed under
6  * the MIT/X11 License. For more information, see
7  * https://geographiclib.sourceforge.io/
8  *
9  * See the <a href="IntersectTool.1.html">man page</a> for usage information.
10  **********************************************************************/
11 
12 #include <iostream>
13 #include <string>
14 #include <sstream>
15 #include <fstream>
16 #include <vector>
18 #include <GeographicLib/DMS.hpp>
21 
22 #include "IntersectTool.usage"
23 using namespace GeographicLib;
24 typedef Math::real real;
25 
26 int main(int argc, const char* const argv[]) {
27  try {
28  enum { CLOSE = 0, OFFSET, NEXT, SEGMENT };
30  real
31  a = Constants::WGS84_a(),
32  f = Constants::WGS84_f(),
33  maxdist = -1;
34  bool exact = false, check = false, longfirst = false;
35  int prec = 3, mode = CLOSE;
36  std::string istring, ifile, ofile, cdelim;
37  char lsep = ';';
38 
39  for (int m = 1; m < argc; ++m) {
40  std::string arg(argv[m]);
41  if (arg == "-e") {
42  if (m + 2 >= argc) return usage(1, true);
43  try {
44  a = Utility::val<real>(std::string(argv[m + 1]));
45  f = Utility::fract<real>(std::string(argv[m + 2]));
46  }
47  catch (const std::exception& e) {
48  std::cerr << "Error decoding arguments of -e: " << e.what() << "\n";
49  return 1;
50  }
51  m += 2;
52  } else if (arg == "-E")
53  exact = true;
54  else if (arg == "-p") {
55  if (++m == argc) return usage(1, true);
56  try {
57  prec = Utility::val<int>(std::string(argv[m]));
58  }
59  catch (const std::exception&) {
60  std::cerr << "Precision " << argv[m] << " is not a number\n";
61  return 1;
62  }
63  } else if (arg == "-R") {
64  if (++m == argc) return usage(1, true);
65  try {
66  maxdist = Utility::val<real>(std::string(argv[m]));
67  }
68  catch (const std::exception&) {
69  std::cerr << "Maxdist " << argv[m] << " is not a number\n";
70  return 1;
71  }
72  if (!(maxdist >= 0)) {
73  std::cerr << "Maxdist must be nonnegative\n";
74  return 1;
75  }
76  } else if (arg == "-c")
77  mode = CLOSE;
78  else if (arg == "-o")
79  mode = OFFSET;
80  else if (arg == "-n")
81  mode = NEXT;
82  else if (arg == "-i")
83  mode = SEGMENT;
84  else if (arg == "-C")
85  check = true;
86  else if (arg == "-w")
87  longfirst = true;
88  else if (arg == "--input-string") {
89  if (++m == argc) return usage(1, true);
90  istring = argv[m];
91  } else if (arg == "--input-file") {
92  if (++m == argc) return usage(1, true);
93  ifile = argv[m];
94  } else if (arg == "--output-file") {
95  if (++m == argc) return usage(1, true);
96  ofile = argv[m];
97  } else if (arg == "--line-separator") {
98  if (++m == argc) return usage(1, true);
99  if (std::string(argv[m]).size() != 1) {
100  std::cerr << "Line separator must be a single character\n";
101  return 1;
102  }
103  lsep = argv[m][0];
104  } else if (arg == "--comment-delimiter") {
105  if (++m == argc) return usage(1, true);
106  cdelim = argv[m];
107  } else if (arg == "--version") {
108  std::cout << argv[0] << ": GeographicLib version "
109  << GEOGRAPHICLIB_VERSION_STRING << "\n";
110  return 0;
111  } else
112  return usage(!(arg == "-h" || arg == "--help"), arg != "--help");
113  }
114 
115  if (!ifile.empty() && !istring.empty()) {
116  std::cerr << "Cannot specify --input-string and --input-file together\n";
117  return 1;
118  }
119  if (ifile == "-") ifile.clear();
120  std::ifstream infile;
121  std::istringstream instring;
122  if (!ifile.empty()) {
123  infile.open(ifile.c_str());
124  if (!infile.is_open()) {
125  std::cerr << "Cannot open " << ifile << " for reading\n";
126  return 1;
127  }
128  } else if (!istring.empty()) {
129  std::string::size_type m = 0;
130  while (true) {
131  m = istring.find(lsep, m);
132  if (m == std::string::npos)
133  break;
134  istring[m] = '\n';
135  }
136  instring.str(istring);
137  }
138  std::istream* input = !ifile.empty() ? &infile :
139  (!istring.empty() ? &instring : &std::cin);
140 
141  std::ofstream outfile;
142  if (ofile == "-") ofile.clear();
143  if (!ofile.empty()) {
144  outfile.open(ofile.c_str());
145  if (!outfile.is_open()) {
146  std::cerr << "Cannot open " << ofile << " for writing\n";
147  return 1;
148  }
149  }
150  std::ostream* output = !ofile.empty() ? &outfile : &std::cout;
151 
152  Geodesic geod(a, f, exact);
153  Intersect intersect(geod);
154  real latX1, lonX1, aziX, latY1, lonY1, aziY, latX2, lonX2, latY2, lonY2,
155  x0 = 0, y0 = 0, x, y;
156  std::string inp[8], s, sc, eol;
157  std::istringstream str;
158  int retval = 0,
159  ninp = mode == CLOSE ? 6 : (mode == NEXT ? 4 :
160  8); // mode == OFFSET || mode == SEGMENT
161  GeodesicLine lineX, lineY;
162  unsigned caps = Intersect::LineCaps;
163  while (std::getline(*input, s)) {
164  try {
165  eol = "\n";
166  if (!cdelim.empty()) {
167  std::string::size_type m = s.find(cdelim);
168  if (m != std::string::npos) {
169  eol = " " + s.substr(m) + "\n";
170  s = s.substr(0, m);
171  }
172  }
173  str.clear(); str.str(s);
174  for (int i = 0; i < ninp; ++i) {
175  if (!(str >> inp[i]))
176  throw GeographicErr("Incomplete input: " + s);
177  }
178  if (str >> sc)
179  throw GeographicErr("Extraneous input: " + sc);
180  if (mode == CLOSE || mode == OFFSET) {
181  DMS::DecodeLatLon(inp[0], inp[1], latX1, lonX1, longfirst);
182  aziX = DMS::DecodeAzimuth(inp[2]);
183  DMS::DecodeLatLon(inp[3], inp[4], latY1, lonY1, longfirst);
184  aziY = DMS::DecodeAzimuth(inp[5]);
185  if (mode == OFFSET) {
186  x0 = Utility::val<real>(inp[6]);
187  y0 = Utility::val<real>(inp[7]);
188  } else
189  x0 = y0 = 0;
190  lineX = geod.Line(latX1, lonX1, aziX, caps);
191  lineY = geod.Line(latY1, lonY1, aziY, caps);
192  } else if (mode == NEXT) {
193  DMS::DecodeLatLon(inp[0], inp[1], latX1, lonX1, longfirst);
194  aziX = DMS::DecodeAzimuth(inp[2]);
195  aziY = DMS::DecodeAzimuth(inp[3]);
196  lineX = geod.Line(latX1, lonX1, aziX, caps);
197  lineY = geod.Line(latX1, lonX1, aziY, caps);
198  } else { // mode == SEGMENT
199  DMS::DecodeLatLon(inp[0], inp[1], latX1, lonX1, longfirst);
200  DMS::DecodeLatLon(inp[2], inp[3], latX2, lonX2, longfirst);
201  DMS::DecodeLatLon(inp[4], inp[5], latY1, lonY1, longfirst);
202  DMS::DecodeLatLon(inp[6], inp[7], latY2, lonY2, longfirst);
203  lineX = geod.InverseLine(latX1, lonX1, latX2, lonX2,
205  lineY = geod.InverseLine(latY1, lonY1, latY2, lonY2,
207  x0 = lineX.Distance()/2;
208  y0 = lineY.Distance()/2;
209  }
210  std::pair<real, real> p0(x0, y0);
211  if (maxdist < 0) {
212  int segmode = 0, c;
213  auto p = mode == CLOSE || mode == OFFSET ?
214  intersect.Closest(lineX, lineY, p0, &c) :
215  mode == NEXT ? intersect.Next(lineX, lineY, &c) :
216  intersect.Segment(lineX, lineY, segmode, &c);
217  x = p.first; y = p.second;
218  *output << Utility::str(x, prec) << " "
219  << Utility::str(y, prec) << " " << c;
220  if (mode == SEGMENT)
221  *output << " " << segmode;
222  *output << eol;
223  if (check) {
224  lineX.Position(x, latX2, lonX2);
225  lineY.Position(y, latY2, lonY2);
226  real sXY;
227  geod.Inverse(latX2, lonX2, latY2, lonY2, sXY);
228  std::cerr << Utility::str(longfirst ? lonX2 : latX2, prec+5) << " "
229  << Utility::str(longfirst ? latX2 : lonX2, prec+5) << " "
230  << Utility::str(longfirst ? lonY2 : latY2, prec+5) << " "
231  << Utility::str(longfirst ? latY2 : lonY2, prec+5) << " "
232  << Utility::str(sXY, prec) << eol;
233  }
234  } else {
235  std::vector<int> c;
236  auto v = intersect.All(lineX, lineY, maxdist, c, p0);
237  unsigned n = unsigned(v.size());
238  for (unsigned i = 0; i < n; ++i) {
239  x = v[i].first; y = v[i].second;
240  *output << Utility::str(x, prec) << " " << Utility::str(y, prec)
241  << " " << c[i] << " "
242  << Utility::str(Intersect::Dist(v[i], p0), prec)
243  << eol;
244  if (check) {
245  lineX.Position(x, latX2, lonX2);
246  lineY.Position(y, latY2, lonY2);
247  real sXY;
248  geod.Inverse(latX2, lonX2, latY2, lonY2, sXY);
249  std::cerr << Utility::str(longfirst ? lonX2 : latX2, prec+5) << " "
250  << Utility::str(longfirst ? latX2 : lonX2, prec+5) << " "
251  << Utility::str(longfirst ? lonY2 : latY2, prec+5) << " "
252  << Utility::str(longfirst ? latY2 : lonY2, prec+5) << " "
253  << Utility::str(sXY, prec) << eol;
254  }
255  }
256  *output << "nan nan 0 nan" << eol;
257  if (check)
258  std::cerr << "nan nan nan nan nan" << eol;
259  }
260  }
261  catch (const std::exception& e) {
262  // Write error message cout so output lines match input lines
263  *output << "ERROR: " << e.what() << "\n";
264  retval = 1;
265  }
266  }
267  return retval;
268  }
269  catch (const std::exception& e) {
270  std::cerr << "Caught exception: " << e.what() << "\n";
271  return 1;
272  }
273  catch (...) {
274  std::cerr << "Caught unknown exception\n";
275  return 1;
276  }
277 }
static const unsigned LineCaps
Definition: Intersect.hpp:84
Geodesic intersections
Definition: Intersect.hpp:71
std::vector< Point > All(Math::real latX, Math::real lonX, Math::real aziX, Math::real latY, Math::real lonY, Math::real aziY, Math::real maxdist, std::vector< int > &c, const Point &p0=Point(0, 0)) const
Definition: Intersect.cpp:115
Math::real real
GeodesicLine Line(real lat1, real lon1, real azi1, unsigned caps=ALL) const
Definition: Geodesic.cpp:123
Header for GeographicLib::Utility class.
static Math::real Dist(const Point &p, const Point &p0=Point(0, 0))
Definition: Intersect.hpp:576
Math::real Position(real s12, real &lat2, real &lon2, real &azi2, real &m12, real &M12, real &M21, real &S12) const
GeodesicLine InverseLine(real lat1, real lon1, real lat2, real lon2, unsigned caps=ALL) const
Definition: Geodesic.cpp:523
Point Next(Math::real latX, Math::real lonX, Math::real aziX, Math::real aziY, int *c=nullptr) const
Definition: Intersect.cpp:91
int usage(int retval, bool)
Definition: Geod3ODE.cpp:41
Math::real Distance() const
Point Closest(Math::real latX, Math::real lonX, Math::real aziX, Math::real latY, Math::real lonY, Math::real aziY, const Point &p0=Point(0, 0), int *c=nullptr) const
Definition: Intersect.cpp:55
Header for GeographicLib::Geodesic class.
Point Segment(Math::real latX1, Math::real lonX1, Math::real latX2, Math::real lonX2, Math::real latY1, Math::real lonY1, Math::real latY2, Math::real lonY2, int &segmode, int *c=nullptr) const
Definition: Intersect.cpp:72
static Math::real DecodeAzimuth(const std::string &azistr)
Definition: DMS.cpp:413
Header for GeographicLib::Intersect class.
int main(int argc, const char *const argv[])
Namespace for GeographicLib.
Definition: Accumulator.cpp:12
static std::string str(T x, int p=-1)
Definition: Utility.hpp:161
static void DecodeLatLon(const std::string &dmsa, const std::string &dmsb, real &lat, real &lon, bool longfirst=false)
Definition: DMS.cpp:374
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
Geodesic calculations
Definition: Geodesic.hpp:175
Math::real Inverse(real lat1, real lon1, real lat2, real lon2, real &s12, real &azi1, real &azi2, real &m12, real &M12, real &M21, real &S12) const
Definition: Geodesic.hpp:689
Header for GeographicLib::DMS class.