1 // waypoint.cxx - waypoints that can occur in routes/procedures
2 // Written by James Turner, started 2009.
4 // Copyright (C) 2009 Curtis L. Olson
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License as
8 // published by the Free Software Foundation; either version 2 of the
9 // License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful, but
12 // WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24 #include "waypoint.hxx"
26 #include <simgear/structure/exception.hxx>
27 #include <simgear/route/waypoint.hxx>
29 #include <Airports/simple.hxx>
30 #include <Airports/runways.hxx>
37 BasicWaypt::BasicWaypt(const SGGeod& aPos, const string& aIdent, Route* aOwner) :
44 BasicWaypt::BasicWaypt(const SGWayPoint& aWP, Route* aOwner) :
46 _pos(aWP.get_target()),
51 BasicWaypt::BasicWaypt(Route* aOwner) :
56 void BasicWaypt::initFromProperties(SGPropertyNode_ptr aProp)
58 if (!aProp->hasChild("lon") || !aProp->hasChild("lat")) {
59 throw sg_io_exception("missing lon/lat properties",
60 "BasicWaypt::initFromProperties");
63 Waypt::initFromProperties(aProp);
65 _pos = SGGeod::fromDeg(aProp->getDoubleValue("lon"),
66 aProp->getDoubleValue("lat"));
67 _ident = aProp->getStringValue("ident");
70 void BasicWaypt::writeToProperties(SGPropertyNode_ptr aProp) const
72 Waypt::writeToProperties(aProp);
74 aProp->setStringValue("ident", _ident);
75 aProp->setDoubleValue("lon", _pos.getLongitudeDeg());
76 aProp->setDoubleValue("lat", _pos.getLatitudeDeg());
79 //////////////////////////////////////////////////////////////////////////////
81 NavaidWaypoint::NavaidWaypoint(FGPositioned* aPos, Route* aOwner) :
85 if (aPos->type() == FGPositioned::RUNWAY) {
86 SG_LOG(SG_GENERAL, SG_WARN, "sure you don't want to be building a runway waypt here?");
90 NavaidWaypoint::NavaidWaypoint(Route* aOwner) :
96 SGGeod NavaidWaypoint::position() const
98 return SGGeod::fromGeodFt(_navaid->geod(), _altitudeFt);
101 std::string NavaidWaypoint::ident() const
103 return _navaid->ident();
106 void NavaidWaypoint::initFromProperties(SGPropertyNode_ptr aProp)
108 if (!aProp->hasChild("ident")) {
109 throw sg_io_exception("missing navaid value",
110 "NavaidWaypoint::initFromProperties");
113 Waypt::initFromProperties(aProp);
115 std::string idn(aProp->getStringValue("ident"));
117 if (aProp->hasChild("lon")) {
118 p = SGGeod::fromDeg(aProp->getDoubleValue("lon"), aProp->getDoubleValue("lat"));
121 // FIXME - resolve co-located DME, etc
122 // is it sufficent just to ignore DMEs, actually?
123 FGPositionedRef nav = FGPositioned::findClosestWithIdent(idn, p, NULL);
125 throw sg_io_exception("unknown navaid ident:" + idn,
126 "NavaidWaypoint::initFromProperties");
132 void NavaidWaypoint::writeToProperties(SGPropertyNode_ptr aProp) const
134 Waypt::writeToProperties(aProp);
136 aProp->setStringValue("ident", _navaid->ident());
137 // write lon/lat to disambiguate
138 aProp->setDoubleValue("lon", _navaid->geod().getLongitudeDeg());
139 aProp->setDoubleValue("lat", _navaid->geod().getLatitudeDeg());
142 OffsetNavaidWaypoint::OffsetNavaidWaypoint(FGPositioned* aPos, Route* aOwner,
143 double aRadial, double aDistNm) :
144 NavaidWaypoint(aPos, aOwner),
151 OffsetNavaidWaypoint::OffsetNavaidWaypoint(Route* aOwner) :
152 NavaidWaypoint(aOwner)
156 void OffsetNavaidWaypoint::init()
160 SGGeodesy::direct(_navaid->geod(), _radial, _distanceNm * SG_NM_TO_METER, offset, az2);
161 _geod = SGGeod::fromGeodFt(offset, _altitudeFt);
164 void OffsetNavaidWaypoint::initFromProperties(SGPropertyNode_ptr aProp)
166 if (!aProp->hasChild("radial-deg") || !aProp->hasChild("distance-nm")) {
167 throw sg_io_exception("missing radial/offset distance",
168 "OffsetNavaidWaypoint::initFromProperties");
171 NavaidWaypoint::initFromProperties(aProp);
172 _radial = aProp->getDoubleValue("radial-deg");
173 _distanceNm = aProp->getDoubleValue("distance-nm");
177 void OffsetNavaidWaypoint::writeToProperties(SGPropertyNode_ptr aProp) const
179 NavaidWaypoint::writeToProperties(aProp);
180 aProp->setDoubleValue("radial-deg", _radial);
181 aProp->setDoubleValue("distance-nm", _distanceNm);
184 /////////////////////////////////////////////////////////////////////////////
186 RunwayWaypt::RunwayWaypt(FGRunway* aPos, Route* aOwner) :
192 RunwayWaypt::RunwayWaypt(Route* aOwner) :
197 SGGeod RunwayWaypt::position() const
199 return _runway->threshold();
202 std::string RunwayWaypt::ident() const
204 return _runway->airport()->ident() + "-" + _runway->ident();
207 FGPositioned* RunwayWaypt::source() const
212 void RunwayWaypt::initFromProperties(SGPropertyNode_ptr aProp)
214 if (!aProp->hasChild("icao") || !aProp->hasChild("ident")) {
215 throw sg_io_exception("missing values: icao or ident",
216 "RunwayWaypoint::initFromProperties");
219 Waypt::initFromProperties(aProp);
220 std::string idn(aProp->getStringValue("ident"));
221 const FGAirport* apt = FGAirport::getByIdent(aProp->getStringValue("icao"));
222 _runway = apt->getRunwayByIdent(aProp->getStringValue("ident"));
225 void RunwayWaypt::writeToProperties(SGPropertyNode_ptr aProp) const
227 Waypt::writeToProperties(aProp);
228 aProp->setStringValue("ident", _runway->ident());
229 aProp->setStringValue("icao", _runway->airport()->ident());
232 /////////////////////////////////////////////////////////////////////////////
234 Hold::Hold(const SGGeod& aPos, const string& aIdent, Route* aOwner) :
235 BasicWaypt(aPos, aIdent, aOwner),
239 setFlag(WPT_DYNAMIC);
242 Hold::Hold(Route* aOwner) :
249 void Hold::setHoldRadial(double aInboundRadial)
251 _bearing = aInboundRadial;
254 void Hold::setHoldDistance(double aDistanceNm)
257 _holdTD = aDistanceNm;
260 void Hold::setHoldTime(double aTimeSec)
266 void Hold::setRightHanded()
271 void Hold::setLeftHanded()
273 _righthanded = false;
276 void Hold::initFromProperties(SGPropertyNode_ptr aProp)
278 BasicWaypt::initFromProperties(aProp);
280 if (!aProp->hasChild("lon") || !aProp->hasChild("lat")) {
281 throw sg_io_exception("missing lon/lat properties",
282 "Hold::initFromProperties");
285 _righthanded = aProp->getBoolValue("right-handed");
286 _isDistance = aProp->getBoolValue("is-distance");
287 _bearing = aProp->getDoubleValue("inbound-radial-deg");
288 _holdTD = aProp->getDoubleValue("td");
291 void Hold::writeToProperties(SGPropertyNode_ptr aProp) const
293 BasicWaypt::writeToProperties(aProp);
295 aProp->setBoolValue("right-handed", _righthanded);
296 aProp->setBoolValue("is-distance", _isDistance);
297 aProp->setDoubleValue("inbound-radial-deg", _bearing);
298 aProp->setDoubleValue("td", _holdTD);
301 /////////////////////////////////////////////////////////////////////////////
303 HeadingToAltitude::HeadingToAltitude(Route* aOwner, const string& aIdent,
309 setFlag(WPT_DYNAMIC);
312 HeadingToAltitude::HeadingToAltitude(Route* aOwner) :
317 void HeadingToAltitude::initFromProperties(SGPropertyNode_ptr aProp)
319 if (!aProp->hasChild("heading-deg")) {
320 throw sg_io_exception("missing heading/alt properties",
321 "HeadingToAltitude::initFromProperties");
324 Waypt::initFromProperties(aProp);
325 _magHeading = aProp->getDoubleValue("heading-deg");
326 _ident = aProp->getStringValue("ident");
329 void HeadingToAltitude::writeToProperties(SGPropertyNode_ptr aProp) const
331 Waypt::writeToProperties(aProp);
332 aProp->setStringValue("ident", _ident);
333 aProp->setDoubleValue("heading-deg", _magHeading);
336 /////////////////////////////////////////////////////////////////////////////
338 DMEIntercept::DMEIntercept(Route* aOwner, const string& aIdent, const SGGeod& aPos,
339 double aCourseDeg, double aDistanceNm) :
343 _magCourse(aCourseDeg),
344 _dmeDistanceNm(aDistanceNm)
346 setFlag(WPT_DYNAMIC);
349 DMEIntercept::DMEIntercept(Route* aOwner) :
354 void DMEIntercept::initFromProperties(SGPropertyNode_ptr aProp)
356 if (!aProp->hasChild("lon") || !aProp->hasChild("lat")) {
357 throw sg_io_exception("missing lon/lat properties",
358 "DMEIntercept::initFromProperties");
361 Waypt::initFromProperties(aProp);
362 _pos = SGGeod::fromDeg(aProp->getDoubleValue("lon"), aProp->getDoubleValue("lat"));
363 _ident = aProp->getStringValue("ident");
364 // check it's a real DME?
365 _magCourse = aProp->getDoubleValue("course-deg");
366 _dmeDistanceNm = aProp->getDoubleValue("dme-distance-nm");
370 void DMEIntercept::writeToProperties(SGPropertyNode_ptr aProp) const
372 Waypt::writeToProperties(aProp);
374 aProp->setStringValue("ident", _ident);
375 aProp->setDoubleValue("lon", _pos.getLongitudeDeg());
376 aProp->setDoubleValue("lat", _pos.getLatitudeDeg());
377 aProp->setDoubleValue("course-deg", _magCourse);
378 aProp->setDoubleValue("dme-distance-nm", _dmeDistanceNm);
381 /////////////////////////////////////////////////////////////////////////////
383 RadialIntercept::RadialIntercept(Route* aOwner, const string& aIdent, const SGGeod& aPos,
384 double aCourseDeg, double aRadial) :
388 _magCourse(aCourseDeg),
391 setFlag(WPT_DYNAMIC);
394 RadialIntercept::RadialIntercept(Route* aOwner) :
399 void RadialIntercept::initFromProperties(SGPropertyNode_ptr aProp)
401 if (!aProp->hasChild("lon") || !aProp->hasChild("lat")) {
402 throw sg_io_exception("missing lon/lat properties",
403 "RadialIntercept::initFromProperties");
406 Waypt::initFromProperties(aProp);
407 _pos = SGGeod::fromDeg(aProp->getDoubleValue("lon"), aProp->getDoubleValue("lat"));
408 _ident = aProp->getStringValue("ident");
409 // check it's a real VOR?
410 _magCourse = aProp->getDoubleValue("course-deg");
411 _radial = aProp->getDoubleValue("radial-deg");
415 void RadialIntercept::writeToProperties(SGPropertyNode_ptr aProp) const
417 Waypt::writeToProperties(aProp);
419 aProp->setStringValue("ident", _ident);
420 aProp->setDoubleValue("lon", _pos.getLongitudeDeg());
421 aProp->setDoubleValue("lat", _pos.getLatitudeDeg());
422 aProp->setDoubleValue("course-deg", _magCourse);
423 aProp->setDoubleValue("radial-deg", _radial);
426 /////////////////////////////////////////////////////////////////////////////
428 ATCVectors::ATCVectors(Route* aOwner, FGAirport* aFacility) :
432 setFlag(WPT_DYNAMIC);
435 ATCVectors::~ATCVectors()
439 ATCVectors::ATCVectors(Route* aOwner) :
444 SGGeod ATCVectors::position() const
446 return _facility->geod();
449 string ATCVectors::ident() const
451 return "VECTORS-" + _facility->ident();
454 void ATCVectors::initFromProperties(SGPropertyNode_ptr aProp)
456 if (!aProp->hasChild("icao")) {
457 throw sg_io_exception("missing icao propertie",
458 "ATCVectors::initFromProperties");
461 Waypt::initFromProperties(aProp);
462 _facility = FGAirport::getByIdent(aProp->getStringValue("icao"));
465 void ATCVectors::writeToProperties(SGPropertyNode_ptr aProp) const
467 Waypt::writeToProperties(aProp);
468 aProp->setStringValue("icao", _facility->ident());