]> git.mxchange.org Git - flightgear.git/blob - src/Navaids/route.cxx
NavData: can refresh some in-place.
[flightgear.git] / src / Navaids / route.cxx
1 // route.cxx - classes supporting waypoints and route structures
2
3 // Written by James Turner, started 2009.
4 //
5 // Copyright (C) 2009  Curtis L. Olson
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
24
25 #include "route.hxx"
26
27 // std
28 #include <map>
29 #include <fstream>
30
31 // Boost
32 #include <boost/algorithm/string/case_conv.hpp>
33 #include <boost/algorithm/string.hpp>
34 #include <boost/foreach.hpp>
35
36 // SimGear
37 #include <simgear/structure/exception.hxx>
38 #include <simgear/misc/sg_path.hxx>
39 #include <simgear/magvar/magvar.hxx>
40 #include <simgear/timing/sg_time.hxx>
41 #include <simgear/misc/sgstream.hxx>
42 #include <simgear/misc/strutils.hxx>
43 #include <simgear/props/props_io.hxx>
44
45 // FlightGear
46 #include <Main/globals.hxx>
47 #include "Main/fg_props.hxx"
48 #include <Navaids/procedure.hxx>
49 #include <Navaids/waypoint.hxx>
50 #include <Navaids/LevelDXML.hxx>
51 #include <Airports/airport.hxx>
52
53 using std::string;
54 using std::vector;
55 using std::endl;
56 using std::fstream;
57
58 namespace flightgear {
59
60 const double NO_MAG_VAR = -1000.0; // an impossible mag-var value
61
62 bool isMachRestrict(RouteRestriction rr)
63 {
64   return (rr == SPEED_RESTRICT_MACH) || (rr == SPEED_COMPUTED_MACH);
65 }
66   
67 Waypt::Waypt(RouteBase* aOwner) :
68   _altitudeFt(0.0),
69   _speed(0.0),
70   _altRestrict(RESTRICT_NONE),
71   _speedRestrict(RESTRICT_NONE),
72   _owner(aOwner),
73   _flags(0),
74   _magVarDeg(NO_MAG_VAR)
75 {
76 }
77
78 Waypt::~Waypt()
79 {
80 }
81   
82 std::string Waypt::ident() const
83 {
84   return "";
85 }
86   
87 bool Waypt::flag(WayptFlag aFlag) const
88 {
89   return ((_flags & aFlag) != 0);
90 }
91         
92 void Waypt::setFlag(WayptFlag aFlag, bool aV)
93 {
94     if (aFlag == 0) {
95         throw sg_range_exception("invalid waypoint flag set");
96     }
97     
98   _flags = (_flags & ~aFlag);
99   if (aV) _flags |= aFlag;
100 }
101
102 bool Waypt::matches(Waypt* aOther) const
103 {
104   assert(aOther);
105   if (ident() != aOther->ident()) { // cheap check first
106     return false;
107   }
108   
109   return matches(aOther->position());
110 }
111
112
113 bool Waypt::matches(const SGGeod& aPos) const
114 {
115   double d = SGGeodesy::distanceM(position(), aPos);
116   return (d < 100.0); // 100 metres seems plenty
117 }
118
119 void Waypt::setAltitude(double aAlt, RouteRestriction aRestrict)
120 {
121   _altitudeFt = aAlt;
122   _altRestrict = aRestrict;
123 }
124
125 void Waypt::setSpeed(double aSpeed, RouteRestriction aRestrict)
126 {
127   _speed = aSpeed;
128   _speedRestrict = aRestrict;
129 }
130
131 double Waypt::speedKts() const
132 {
133   assert(_speedRestrict != SPEED_RESTRICT_MACH);
134   return speed();
135 }
136   
137 double Waypt::speedMach() const
138 {
139   assert(_speedRestrict == SPEED_RESTRICT_MACH);
140   return speed();
141 }
142   
143 std::pair<double, double>
144 Waypt::courseAndDistanceFrom(const SGGeod& aPos) const
145 {
146   if (flag(WPT_DYNAMIC)) {
147     return std::make_pair(0.0, 0.0);
148   }
149   
150   double course, az2, distance;
151   SGGeodesy::inverse(aPos, position(), course, az2, distance);
152   return std::make_pair(course, distance);
153 }
154
155 double Waypt::magvarDeg() const
156 {
157   if (_magVarDeg == NO_MAG_VAR) {
158     // derived classes with a default pos must override this method
159     assert(!(position() == SGGeod()));
160     
161     double jd = globals->get_time_params()->getJD();
162     _magVarDeg = sgGetMagVar(position(), jd) * SG_RADIANS_TO_DEGREES;
163   }
164   
165   return _magVarDeg;
166 }
167   
168 double Waypt::headingRadialDeg() const
169 {
170   return 0.0;
171 }
172   
173 ///////////////////////////////////////////////////////////////////////////
174 // persistence
175
176 static RouteRestriction restrictionFromString(const char* aStr)
177 {
178   std::string l = boost::to_lower_copy(std::string(aStr));
179
180   if (l == "at") return RESTRICT_AT;
181   if (l == "above") return RESTRICT_ABOVE;
182   if (l == "below") return RESTRICT_BELOW;
183   if (l == "none") return RESTRICT_NONE;
184   if (l == "mach") return SPEED_RESTRICT_MACH;
185   
186   if (l.empty()) return RESTRICT_NONE;
187   throw sg_io_exception("unknown restriction specification:" + l, 
188     "Route restrictFromString");
189 }
190
191 static const char* restrictionToString(RouteRestriction aRestrict)
192 {
193   switch (aRestrict) {
194   case RESTRICT_AT: return "at";
195   case RESTRICT_BELOW: return "below";
196   case RESTRICT_ABOVE: return "above";
197   case RESTRICT_NONE: return "none";
198   case SPEED_RESTRICT_MACH: return "mach";
199   
200   default:
201     throw sg_exception("invalid route restriction",
202       "Route restrictToString");
203   }
204 }
205
206 Waypt* Waypt::createInstance(RouteBase* aOwner, const std::string& aTypeName)
207 {
208   Waypt* r = NULL;
209   if (aTypeName == "basic") {
210     r = new BasicWaypt(aOwner);
211   } else if (aTypeName == "navaid") {
212     r = new NavaidWaypoint(aOwner);
213   } else if (aTypeName == "offset-navaid") {
214     r = new OffsetNavaidWaypoint(aOwner);
215   } else if (aTypeName == "hold") {
216     r = new Hold(aOwner);
217   } else if (aTypeName == "runway") {
218     r = new RunwayWaypt(aOwner);
219   } else if (aTypeName == "hdgToAlt") {
220     r = new HeadingToAltitude(aOwner);
221   } else if (aTypeName == "dmeIntercept") {
222     r = new DMEIntercept(aOwner);
223   } else if (aTypeName == "radialIntercept") {
224     r = new RadialIntercept(aOwner);
225   } else if (aTypeName == "vectors") {
226     r = new ATCVectors(aOwner);
227   } 
228
229   if (!r || (r->type() != aTypeName)) {
230     throw sg_exception("broken factory method for type:" + aTypeName,
231       "Waypt::createInstance");
232   }
233   
234   return r;
235 }
236
237 WayptRef Waypt::createFromProperties(RouteBase* aOwner, SGPropertyNode_ptr aProp)
238 {
239   if (!aProp->hasChild("type")) {
240     throw sg_io_exception("bad props node, no type provided", 
241       "Waypt::createFromProperties");
242   }
243   
244   try {
245     WayptRef nd(createInstance(aOwner, aProp->getStringValue("type")));
246     nd->initFromProperties(aProp);
247     return nd;
248   } catch (sg_exception& e) {
249     SG_LOG(SG_GENERAL, SG_WARN, "failed to create waypoint, trying basic:" << e.getMessage());
250   }
251   
252 // if we failed to make the waypoint, try again making a basic waypoint.
253 // this handles the case where a navaid waypoint is missing, for example
254   WayptRef nd(new BasicWaypt(aOwner));
255   nd->initFromProperties(aProp);
256   return nd;
257 }
258   
259 void Waypt::saveAsNode(SGPropertyNode* n) const
260 {
261   n->setStringValue("type", type());
262   writeToProperties(n);
263 }
264
265 void Waypt::initFromProperties(SGPropertyNode_ptr aProp)
266 {
267   if (aProp->hasChild("generated")) {
268     setFlag(WPT_GENERATED, aProp->getBoolValue("generated")); 
269   }
270   
271   if (aProp->hasChild("overflight")) {
272     setFlag(WPT_OVERFLIGHT, aProp->getBoolValue("overflight")); 
273   }
274   
275   if (aProp->hasChild("arrival")) {
276     setFlag(WPT_ARRIVAL, aProp->getBoolValue("arrival")); 
277   }
278   
279   if (aProp->hasChild("approach")) {
280     setFlag(WPT_APPROACH, aProp->getBoolValue("approach"));
281   }
282   
283   if (aProp->hasChild("departure")) {
284     setFlag(WPT_DEPARTURE, aProp->getBoolValue("departure")); 
285   }
286   
287   if (aProp->hasChild("miss")) {
288     setFlag(WPT_MISS, aProp->getBoolValue("miss")); 
289   }
290   
291   if (aProp->hasChild("alt-restrict")) {
292     _altRestrict = restrictionFromString(aProp->getStringValue("alt-restrict"));
293     _altitudeFt = aProp->getDoubleValue("altitude-ft");
294   }
295   
296   if (aProp->hasChild("speed-restrict")) {
297     _speedRestrict = restrictionFromString(aProp->getStringValue("speed-restrict"));
298     _speed = aProp->getDoubleValue("speed");
299   }
300   
301   
302 }
303
304 void Waypt::writeToProperties(SGPropertyNode_ptr aProp) const
305 {
306   if (flag(WPT_OVERFLIGHT)) {
307     aProp->setBoolValue("overflight", true);
308   }
309
310   if (flag(WPT_DEPARTURE)) {
311     aProp->setBoolValue("departure", true);
312   }
313   
314   if (flag(WPT_ARRIVAL)) {
315     aProp->setBoolValue("arrival", true);
316   }
317   
318   if (flag(WPT_APPROACH)) {
319     aProp->setBoolValue("approach", true);
320   }
321   
322   if (flag(WPT_MISS)) {
323     aProp->setBoolValue("miss", true);
324   }
325   
326   if (flag(WPT_GENERATED)) {
327     aProp->setBoolValue("generated", true);
328   }
329   
330   if (_altRestrict != RESTRICT_NONE) {
331     aProp->setStringValue("alt-restrict", restrictionToString(_altRestrict));
332     aProp->setDoubleValue("altitude-ft", _altitudeFt);
333   }
334   
335   if (_speedRestrict != RESTRICT_NONE) {
336     aProp->setStringValue("speed-restrict", restrictionToString(_speedRestrict));
337     aProp->setDoubleValue("speed", _speed);
338   }
339 }
340
341 void RouteBase::dumpRouteToKML(const WayptVec& aRoute, const std::string& aName)
342 {
343   SGPath p = "/Users/jmt/Desktop/" + aName + ".kml";
344   std::fstream f;
345   f.open(p.str().c_str(), fstream::out | fstream::app);
346   if (!f.is_open()) {
347     SG_LOG(SG_NAVAID, SG_WARN, "unable to open:" << p.str());
348     return;
349   }
350   
351 // pre-amble
352   f << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
353       "<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n"
354     "<Document>\n";
355
356   dumpRouteToKMLLineString(aName, aRoute, f);
357   
358 // post-amble
359   f << "</Document>\n" 
360     "</kml>" << endl;
361   f.close();
362 }
363
364 void RouteBase::dumpRouteToKMLLineString(const std::string& aIdent,
365   const WayptVec& aRoute, std::ostream& aStream)
366 {
367   // preamble
368   aStream << "<Placemark>\n";
369   aStream << "<name>" << aIdent << "</name>\n";
370   aStream << "<LineString>\n";
371   aStream << "<tessellate>1</tessellate>\n";
372   aStream << "<coordinates>\n";
373   
374   // waypoints
375   for (unsigned int i=0; i<aRoute.size(); ++i) {
376     SGGeod pos = aRoute[i]->position();
377     aStream << pos.getLongitudeDeg() << "," << pos.getLatitudeDeg() << " " << endl;
378   }
379   
380   // postable
381   aStream << "</coordinates>\n"
382     "</LineString>\n"
383     "</Placemark>\n" << endl;
384 }
385
386 void RouteBase::loadAirportProcedures(const SGPath& aPath, FGAirport* aApt)
387 {
388   assert(aApt);
389   try {
390     NavdataVisitor visitor(aApt, aPath);
391     readXML(aPath.str(), visitor);
392   } catch (sg_io_exception& ex) {
393     SG_LOG(SG_NAVAID, SG_WARN, "failure parsing procedures: " << aPath.str() <<
394       "\n\t" << ex.getMessage() << "\n\tat:" << ex.getLocation().asString());
395   } catch (sg_exception& ex) {
396     SG_LOG(SG_NAVAID, SG_WARN, "failure parsing procedures: " << aPath.str() <<
397       "\n\t" << ex.getMessage());
398   }
399 }
400   
401 } // of namespace flightgear