1 // route.cxx - classes supporting waypoints and route structures
3 // Written by James Turner, started 2009.
5 // Copyright (C) 2009 Curtis L. Olson
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.
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.
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.
32 #include <boost/algorithm/string/case_conv.hpp>
33 #include <boost/algorithm/string.hpp>
34 #include <boost/foreach.hpp>
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>
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/simple.hxx>
58 namespace flightgear {
60 const double NO_MAG_VAR = -1000.0; // an impossible mag-var value
62 bool isMachRestrict(RouteRestriction rr)
64 return (rr == SPEED_RESTRICT_MACH) || (rr == SPEED_COMPUTED_MACH);
67 Waypt::Waypt(RouteBase* aOwner) :
70 _altRestrict(RESTRICT_NONE),
71 _speedRestrict(RESTRICT_NONE),
74 _magVarDeg(NO_MAG_VAR)
82 std::string Waypt::ident() const
87 bool Waypt::flag(WayptFlag aFlag) const
89 return ((_flags & aFlag) != 0);
92 void Waypt::setFlag(WayptFlag aFlag, bool aV)
94 _flags = (_flags & ~aFlag);
95 if (aV) _flags |= aFlag;
98 bool Waypt::matches(Waypt* aOther) const
101 if (ident() != aOther->ident()) { // cheap check first
105 return matches(aOther->position());
109 bool Waypt::matches(const SGGeod& aPos) const
111 double d = SGGeodesy::distanceM(position(), aPos);
112 return (d < 100.0); // 100 metres seems plenty
115 void Waypt::setAltitude(double aAlt, RouteRestriction aRestrict)
118 _altRestrict = aRestrict;
121 void Waypt::setSpeed(double aSpeed, RouteRestriction aRestrict)
124 _speedRestrict = aRestrict;
127 double Waypt::speedKts() const
129 assert(_speedRestrict != SPEED_RESTRICT_MACH);
133 double Waypt::speedMach() const
135 assert(_speedRestrict == SPEED_RESTRICT_MACH);
139 std::pair<double, double>
140 Waypt::courseAndDistanceFrom(const SGGeod& aPos) const
142 if (flag(WPT_DYNAMIC)) {
143 return std::make_pair(0.0, 0.0);
146 double course, az2, distance;
147 SGGeodesy::inverse(aPos, position(), course, az2, distance);
148 return std::make_pair(course, distance);
151 double Waypt::magvarDeg() const
153 if (_magVarDeg == NO_MAG_VAR) {
154 // derived classes with a default pos must override this method
155 assert(!(position() == SGGeod()));
157 double jd = globals->get_time_params()->getJD();
158 _magVarDeg = sgGetMagVar(position(), jd) * SG_RADIANS_TO_DEGREES;
164 double Waypt::headingRadialDeg() const
169 ///////////////////////////////////////////////////////////////////////////
172 static RouteRestriction restrictionFromString(const char* aStr)
174 std::string l = boost::to_lower_copy(std::string(aStr));
176 if (l == "at") return RESTRICT_AT;
177 if (l == "above") return RESTRICT_ABOVE;
178 if (l == "below") return RESTRICT_BELOW;
179 if (l == "none") return RESTRICT_NONE;
180 if (l == "mach") return SPEED_RESTRICT_MACH;
182 if (l.empty()) return RESTRICT_NONE;
183 throw sg_io_exception("unknown restriction specification:" + l,
184 "Route restrictFromString");
187 static const char* restrictionToString(RouteRestriction aRestrict)
190 case RESTRICT_AT: return "at";
191 case RESTRICT_BELOW: return "below";
192 case RESTRICT_ABOVE: return "above";
193 case RESTRICT_NONE: return "none";
194 case SPEED_RESTRICT_MACH: return "mach";
197 throw sg_exception("invalid route restriction",
198 "Route restrictToString");
202 Waypt* Waypt::createInstance(RouteBase* aOwner, const std::string& aTypeName)
205 if (aTypeName == "basic") {
206 r = new BasicWaypt(aOwner);
207 } else if (aTypeName == "navaid") {
208 r = new NavaidWaypoint(aOwner);
209 } else if (aTypeName == "offset-navaid") {
210 r = new OffsetNavaidWaypoint(aOwner);
211 } else if (aTypeName == "hold") {
212 r = new Hold(aOwner);
213 } else if (aTypeName == "runway") {
214 r = new RunwayWaypt(aOwner);
215 } else if (aTypeName == "hdgToAlt") {
216 r = new HeadingToAltitude(aOwner);
217 } else if (aTypeName == "dmeIntercept") {
218 r = new DMEIntercept(aOwner);
219 } else if (aTypeName == "radialIntercept") {
220 r = new RadialIntercept(aOwner);
221 } else if (aTypeName == "vectors") {
222 r = new ATCVectors(aOwner);
225 if (!r || (r->type() != aTypeName)) {
226 throw sg_exception("broken factory method for type:" + aTypeName,
227 "Waypt::createInstance");
233 WayptRef Waypt::createFromProperties(RouteBase* aOwner, SGPropertyNode_ptr aProp)
235 if (!aProp->hasChild("type")) {
236 throw sg_io_exception("bad props node, no type provided",
237 "Waypt::createFromProperties");
240 WayptRef nd(createInstance(aOwner, aProp->getStringValue("type")));
241 nd->initFromProperties(aProp);
245 void Waypt::saveAsNode(SGPropertyNode* n) const
247 n->setStringValue("type", type());
248 writeToProperties(n);
251 void Waypt::initFromProperties(SGPropertyNode_ptr aProp)
253 if (aProp->hasChild("generated")) {
254 setFlag(WPT_GENERATED, aProp->getBoolValue("generated"));
257 if (aProp->hasChild("overflight")) {
258 setFlag(WPT_OVERFLIGHT, aProp->getBoolValue("overflight"));
261 if (aProp->hasChild("arrival")) {
262 setFlag(WPT_ARRIVAL, aProp->getBoolValue("arrival"));
265 if (aProp->hasChild("approach")) {
266 setFlag(WPT_APPROACH, aProp->getBoolValue("approach"));
269 if (aProp->hasChild("departure")) {
270 setFlag(WPT_DEPARTURE, aProp->getBoolValue("departure"));
273 if (aProp->hasChild("miss")) {
274 setFlag(WPT_MISS, aProp->getBoolValue("miss"));
277 if (aProp->hasChild("alt-restrict")) {
278 _altRestrict = restrictionFromString(aProp->getStringValue("alt-restrict"));
279 _altitudeFt = aProp->getDoubleValue("altitude-ft");
282 if (aProp->hasChild("speed-restrict")) {
283 _speedRestrict = restrictionFromString(aProp->getStringValue("speed-restrict"));
284 _speed = aProp->getDoubleValue("speed");
290 void Waypt::writeToProperties(SGPropertyNode_ptr aProp) const
292 if (flag(WPT_OVERFLIGHT)) {
293 aProp->setBoolValue("overflight", true);
296 if (flag(WPT_DEPARTURE)) {
297 aProp->setBoolValue("departure", true);
300 if (flag(WPT_ARRIVAL)) {
301 aProp->setBoolValue("arrival", true);
304 if (flag(WPT_APPROACH)) {
305 aProp->setBoolValue("approach", true);
308 if (flag(WPT_MISS)) {
309 aProp->setBoolValue("miss", true);
312 if (flag(WPT_GENERATED)) {
313 aProp->setBoolValue("generated", true);
316 if (_altRestrict != RESTRICT_NONE) {
317 aProp->setStringValue("alt-restrict", restrictionToString(_altRestrict));
318 aProp->setDoubleValue("altitude-ft", _altitudeFt);
321 if (_speedRestrict != RESTRICT_NONE) {
322 aProp->setStringValue("speed-restrict", restrictionToString(_speedRestrict));
323 aProp->setDoubleValue("speed", _speed);
327 void RouteBase::dumpRouteToKML(const WayptVec& aRoute, const std::string& aName)
329 SGPath p = "/Users/jmt/Desktop/" + aName + ".kml";
331 f.open(p.str().c_str(), fstream::out | fstream::app);
333 SG_LOG(SG_GENERAL, SG_WARN, "unable to open:" << p.str());
338 f << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
339 "<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n"
342 dumpRouteToKMLLineString(aName, aRoute, f);
350 void RouteBase::dumpRouteToKMLLineString(const std::string& aIdent,
351 const WayptVec& aRoute, std::ostream& aStream)
354 aStream << "<Placemark>\n";
355 aStream << "<name>" << aIdent << "</name>\n";
356 aStream << "<LineString>\n";
357 aStream << "<tessellate>1</tessellate>\n";
358 aStream << "<coordinates>\n";
361 for (unsigned int i=0; i<aRoute.size(); ++i) {
362 SGGeod pos = aRoute[i]->position();
363 aStream << pos.getLongitudeDeg() << "," << pos.getLatitudeDeg() << " " << endl;
367 aStream << "</coordinates>\n"
369 "</Placemark>\n" << endl;
372 void RouteBase::loadAirportProcedures(const SGPath& aPath, FGAirport* aApt)
376 NavdataVisitor visitor(aApt, aPath);
377 readXML(aPath.str(), visitor);
378 } catch (sg_io_exception& ex) {
379 SG_LOG(SG_GENERAL, SG_WARN, "failure parsing procedures: " << aPath.str() <<
380 "\n\t" << ex.getMessage() << "\n\tat:" << ex.getLocation().asString());
381 } catch (sg_exception& ex) {
382 SG_LOG(SG_GENERAL, SG_WARN, "failure parsing procedures: " << aPath.str() <<
383 "\n\t" << ex.getMessage());
387 } // of namespace flightgear