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/airport.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)
95 throw sg_range_exception("invalid waypoint flag set");
98 _flags = (_flags & ~aFlag);
99 if (aV) _flags |= aFlag;
102 bool Waypt::matches(Waypt* aOther) const
105 if (ident() != aOther->ident()) { // cheap check first
109 return matches(aOther->position());
113 bool Waypt::matches(const SGGeod& aPos) const
115 double d = SGGeodesy::distanceM(position(), aPos);
116 return (d < 100.0); // 100 metres seems plenty
119 void Waypt::setAltitude(double aAlt, RouteRestriction aRestrict)
122 _altRestrict = aRestrict;
125 void Waypt::setSpeed(double aSpeed, RouteRestriction aRestrict)
128 _speedRestrict = aRestrict;
131 double Waypt::speedKts() const
133 assert(_speedRestrict != SPEED_RESTRICT_MACH);
137 double Waypt::speedMach() const
139 assert(_speedRestrict == SPEED_RESTRICT_MACH);
143 double Waypt::magvarDeg() const
145 if (_magVarDeg == NO_MAG_VAR) {
146 // derived classes with a default pos must override this method
147 assert(!(position() == SGGeod()));
149 double jd = globals->get_time_params()->getJD();
150 _magVarDeg = sgGetMagVar(position(), jd) * SG_RADIANS_TO_DEGREES;
156 double Waypt::headingRadialDeg() const
161 ///////////////////////////////////////////////////////////////////////////
164 static RouteRestriction restrictionFromString(const char* aStr)
166 std::string l = boost::to_lower_copy(std::string(aStr));
168 if (l == "at") return RESTRICT_AT;
169 if (l == "above") return RESTRICT_ABOVE;
170 if (l == "below") return RESTRICT_BELOW;
171 if (l == "none") return RESTRICT_NONE;
172 if (l == "mach") return SPEED_RESTRICT_MACH;
174 if (l.empty()) return RESTRICT_NONE;
175 throw sg_io_exception("unknown restriction specification:" + l,
176 "Route restrictFromString");
179 static const char* restrictionToString(RouteRestriction aRestrict)
182 case RESTRICT_AT: return "at";
183 case RESTRICT_BELOW: return "below";
184 case RESTRICT_ABOVE: return "above";
185 case RESTRICT_NONE: return "none";
186 case SPEED_RESTRICT_MACH: return "mach";
189 throw sg_exception("invalid route restriction",
190 "Route restrictToString");
194 Waypt* Waypt::createInstance(RouteBase* aOwner, const std::string& aTypeName)
197 if (aTypeName == "basic") {
198 r = new BasicWaypt(aOwner);
199 } else if (aTypeName == "navaid") {
200 r = new NavaidWaypoint(aOwner);
201 } else if (aTypeName == "offset-navaid") {
202 r = new OffsetNavaidWaypoint(aOwner);
203 } else if (aTypeName == "hold") {
204 r = new Hold(aOwner);
205 } else if (aTypeName == "runway") {
206 r = new RunwayWaypt(aOwner);
207 } else if (aTypeName == "hdgToAlt") {
208 r = new HeadingToAltitude(aOwner);
209 } else if (aTypeName == "dmeIntercept") {
210 r = new DMEIntercept(aOwner);
211 } else if (aTypeName == "radialIntercept") {
212 r = new RadialIntercept(aOwner);
213 } else if (aTypeName == "vectors") {
214 r = new ATCVectors(aOwner);
217 if (!r || (r->type() != aTypeName)) {
218 throw sg_exception("broken factory method for type:" + aTypeName,
219 "Waypt::createInstance");
225 WayptRef Waypt::createFromProperties(RouteBase* aOwner, SGPropertyNode_ptr aProp)
227 if (!aProp->hasChild("type")) {
228 throw sg_io_exception("bad props node, no type provided",
229 "Waypt::createFromProperties");
233 WayptRef nd(createInstance(aOwner, aProp->getStringValue("type")));
234 nd->initFromProperties(aProp);
236 } catch (sg_exception& e) {
237 SG_LOG(SG_GENERAL, SG_WARN, "failed to create waypoint, trying basic:" << e.getMessage());
240 // if we failed to make the waypoint, try again making a basic waypoint.
241 // this handles the case where a navaid waypoint is missing, for example
242 WayptRef nd(new BasicWaypt(aOwner));
243 nd->initFromProperties(aProp);
247 void Waypt::saveAsNode(SGPropertyNode* n) const
249 n->setStringValue("type", type());
250 writeToProperties(n);
253 void Waypt::initFromProperties(SGPropertyNode_ptr aProp)
255 if (aProp->hasChild("generated")) {
256 setFlag(WPT_GENERATED, aProp->getBoolValue("generated"));
259 if (aProp->hasChild("overflight")) {
260 setFlag(WPT_OVERFLIGHT, aProp->getBoolValue("overflight"));
263 if (aProp->hasChild("arrival")) {
264 setFlag(WPT_ARRIVAL, aProp->getBoolValue("arrival"));
267 if (aProp->hasChild("approach")) {
268 setFlag(WPT_APPROACH, aProp->getBoolValue("approach"));
271 if (aProp->hasChild("departure")) {
272 setFlag(WPT_DEPARTURE, aProp->getBoolValue("departure"));
275 if (aProp->hasChild("miss")) {
276 setFlag(WPT_MISS, aProp->getBoolValue("miss"));
279 if (aProp->hasChild("alt-restrict")) {
280 _altRestrict = restrictionFromString(aProp->getStringValue("alt-restrict"));
281 _altitudeFt = aProp->getDoubleValue("altitude-ft");
284 if (aProp->hasChild("speed-restrict")) {
285 _speedRestrict = restrictionFromString(aProp->getStringValue("speed-restrict"));
286 _speed = aProp->getDoubleValue("speed");
292 void Waypt::writeToProperties(SGPropertyNode_ptr aProp) const
294 if (flag(WPT_OVERFLIGHT)) {
295 aProp->setBoolValue("overflight", true);
298 if (flag(WPT_DEPARTURE)) {
299 aProp->setBoolValue("departure", true);
302 if (flag(WPT_ARRIVAL)) {
303 aProp->setBoolValue("arrival", true);
306 if (flag(WPT_APPROACH)) {
307 aProp->setBoolValue("approach", true);
310 if (flag(WPT_MISS)) {
311 aProp->setBoolValue("miss", true);
314 if (flag(WPT_GENERATED)) {
315 aProp->setBoolValue("generated", true);
318 if (_altRestrict != RESTRICT_NONE) {
319 aProp->setStringValue("alt-restrict", restrictionToString(_altRestrict));
320 aProp->setDoubleValue("altitude-ft", _altitudeFt);
323 if (_speedRestrict != RESTRICT_NONE) {
324 aProp->setStringValue("speed-restrict", restrictionToString(_speedRestrict));
325 aProp->setDoubleValue("speed", _speed);
329 void RouteBase::dumpRouteToKML(const WayptVec& aRoute, const std::string& aName)
331 SGPath p = "/Users/jmt/Desktop/" + aName + ".kml";
333 f.open(p.str().c_str(), fstream::out | fstream::app);
335 SG_LOG(SG_NAVAID, SG_WARN, "unable to open:" << p.str());
340 f << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
341 "<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n"
344 dumpRouteToKMLLineString(aName, aRoute, f);
352 void RouteBase::dumpRouteToKMLLineString(const std::string& aIdent,
353 const WayptVec& aRoute, std::ostream& aStream)
356 aStream << "<Placemark>\n";
357 aStream << "<name>" << aIdent << "</name>\n";
358 aStream << "<LineString>\n";
359 aStream << "<tessellate>1</tessellate>\n";
360 aStream << "<coordinates>\n";
363 for (unsigned int i=0; i<aRoute.size(); ++i) {
364 SGGeod pos = aRoute[i]->position();
365 aStream << pos.getLongitudeDeg() << "," << pos.getLatitudeDeg() << " " << endl;
369 aStream << "</coordinates>\n"
371 "</Placemark>\n" << endl;
374 void RouteBase::loadAirportProcedures(const SGPath& aPath, FGAirport* aApt)
378 NavdataVisitor visitor(aApt, aPath);
379 readXML(aPath.str(), visitor);
380 } catch (sg_io_exception& ex) {
381 SG_LOG(SG_NAVAID, SG_WARN, "failure parsing procedures: " << aPath.str() <<
382 "\n\t" << ex.getMessage() << "\n\tat:" << ex.getLocation().asString());
383 } catch (sg_exception& ex) {
384 SG_LOG(SG_NAVAID, SG_WARN, "failure parsing procedures: " << aPath.str() <<
385 "\n\t" << ex.getMessage());
389 } // of namespace flightgear