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>
28 #include <Airports/airport.hxx>
29 #include <Airports/runways.hxx>
30 #include <Navaids/airways.hxx>
37 BasicWaypt::BasicWaypt(const SGGeod& aPos, const string& aIdent, RouteBase* aOwner) :
44 BasicWaypt::BasicWaypt(RouteBase* aOwner) :
49 void BasicWaypt::initFromProperties(SGPropertyNode_ptr aProp)
51 if (!aProp->hasChild("lon") || !aProp->hasChild("lat")) {
52 throw sg_io_exception("missing lon/lat properties",
53 "BasicWaypt::initFromProperties");
56 Waypt::initFromProperties(aProp);
58 _pos = SGGeod::fromDeg(aProp->getDoubleValue("lon"),
59 aProp->getDoubleValue("lat"));
60 _ident = aProp->getStringValue("ident");
63 void BasicWaypt::writeToProperties(SGPropertyNode_ptr aProp) const
65 Waypt::writeToProperties(aProp);
67 aProp->setStringValue("ident", _ident);
68 aProp->setDoubleValue("lon", _pos.getLongitudeDeg());
69 aProp->setDoubleValue("lat", _pos.getLatitudeDeg());
72 //////////////////////////////////////////////////////////////////////////////
74 NavaidWaypoint::NavaidWaypoint(FGPositioned* aPos, RouteBase* aOwner) :
78 if (aPos->type() == FGPositioned::RUNWAY) {
79 SG_LOG(SG_NAVAID, SG_WARN, "sure you don't want to be building a runway waypt here?");
83 NavaidWaypoint::NavaidWaypoint(RouteBase* aOwner) :
89 SGGeod NavaidWaypoint::position() const
91 return SGGeod::fromGeodFt(_navaid->geod(), _altitudeFt);
94 std::string NavaidWaypoint::ident() const
96 return _navaid->ident();
99 void NavaidWaypoint::initFromProperties(SGPropertyNode_ptr aProp)
101 if (!aProp->hasChild("ident")) {
102 throw sg_io_exception("missing navaid value",
103 "NavaidWaypoint::initFromProperties");
106 Waypt::initFromProperties(aProp);
108 std::string idn(aProp->getStringValue("ident"));
110 if (aProp->hasChild("lon")) {
111 p = SGGeod::fromDeg(aProp->getDoubleValue("lon"), aProp->getDoubleValue("lat"));
114 // FIXME - resolve co-located DME, etc
115 // is it sufficent just to ignore DMEs, actually?
116 FGPositionedRef nav = FGPositioned::findClosestWithIdent(idn, p, NULL);
118 throw sg_io_exception("unknown navaid ident:" + idn,
119 "NavaidWaypoint::initFromProperties");
125 void NavaidWaypoint::writeToProperties(SGPropertyNode_ptr aProp) const
127 Waypt::writeToProperties(aProp);
129 aProp->setStringValue("ident", _navaid->ident());
130 // write lon/lat to disambiguate
131 aProp->setDoubleValue("lon", _navaid->geod().getLongitudeDeg());
132 aProp->setDoubleValue("lat", _navaid->geod().getLatitudeDeg());
135 OffsetNavaidWaypoint::OffsetNavaidWaypoint(FGPositioned* aPos, RouteBase* aOwner,
136 double aRadial, double aDistNm) :
137 NavaidWaypoint(aPos, aOwner),
144 OffsetNavaidWaypoint::OffsetNavaidWaypoint(RouteBase* aOwner) :
145 NavaidWaypoint(aOwner)
149 void OffsetNavaidWaypoint::init()
153 SGGeodesy::direct(_navaid->geod(), _radial, _distanceNm * SG_NM_TO_METER, offset, az2);
154 _geod = SGGeod::fromGeodFt(offset, _altitudeFt);
157 void OffsetNavaidWaypoint::initFromProperties(SGPropertyNode_ptr aProp)
159 if (!aProp->hasChild("radial-deg") || !aProp->hasChild("distance-nm")) {
160 throw sg_io_exception("missing radial/offset distance",
161 "OffsetNavaidWaypoint::initFromProperties");
164 NavaidWaypoint::initFromProperties(aProp);
165 _radial = aProp->getDoubleValue("radial-deg");
166 _distanceNm = aProp->getDoubleValue("distance-nm");
170 void OffsetNavaidWaypoint::writeToProperties(SGPropertyNode_ptr aProp) const
172 NavaidWaypoint::writeToProperties(aProp);
173 aProp->setDoubleValue("radial-deg", _radial);
174 aProp->setDoubleValue("distance-nm", _distanceNm);
177 /////////////////////////////////////////////////////////////////////////////
179 RunwayWaypt::RunwayWaypt(FGRunway* aPos, RouteBase* aOwner) :
185 RunwayWaypt::RunwayWaypt(RouteBase* aOwner) :
190 SGGeod RunwayWaypt::position() const
192 return _runway->threshold();
195 std::string RunwayWaypt::ident() const
197 return _runway->airport()->ident() + "-" + _runway->ident();
200 FGPositioned* RunwayWaypt::source() const
205 double RunwayWaypt::headingRadialDeg() const
207 return _runway->headingDeg();
210 void RunwayWaypt::initFromProperties(SGPropertyNode_ptr aProp)
212 if (!aProp->hasChild("icao") || !aProp->hasChild("ident")) {
213 throw sg_io_exception("missing values: icao or ident",
214 "RunwayWaypoint::initFromProperties");
217 Waypt::initFromProperties(aProp);
218 std::string idn(aProp->getStringValue("ident"));
219 const FGAirport* apt = FGAirport::getByIdent(aProp->getStringValue("icao"));
220 _runway = apt->getRunwayByIdent(aProp->getStringValue("ident"));
223 void RunwayWaypt::writeToProperties(SGPropertyNode_ptr aProp) const
225 Waypt::writeToProperties(aProp);
226 aProp->setStringValue("ident", _runway->ident());
227 aProp->setStringValue("icao", _runway->airport()->ident());
230 /////////////////////////////////////////////////////////////////////////////
232 Hold::Hold(const SGGeod& aPos, const string& aIdent, RouteBase* aOwner) :
233 BasicWaypt(aPos, aIdent, aOwner),
237 setFlag(WPT_DYNAMIC);
240 Hold::Hold(RouteBase* aOwner) :
247 void Hold::setHoldRadial(double aInboundRadial)
249 _bearing = aInboundRadial;
252 void Hold::setHoldDistance(double aDistanceNm)
255 _holdTD = aDistanceNm;
258 void Hold::setHoldTime(double aTimeSec)
264 void Hold::setRightHanded()
269 void Hold::setLeftHanded()
271 _righthanded = false;
274 void Hold::initFromProperties(SGPropertyNode_ptr aProp)
276 BasicWaypt::initFromProperties(aProp);
278 if (!aProp->hasChild("lon") || !aProp->hasChild("lat")) {
279 throw sg_io_exception("missing lon/lat properties",
280 "Hold::initFromProperties");
283 _righthanded = aProp->getBoolValue("right-handed");
284 _isDistance = aProp->getBoolValue("is-distance");
285 _bearing = aProp->getDoubleValue("inbound-radial-deg");
286 _holdTD = aProp->getDoubleValue("td");
289 void Hold::writeToProperties(SGPropertyNode_ptr aProp) const
291 BasicWaypt::writeToProperties(aProp);
293 aProp->setBoolValue("right-handed", _righthanded);
294 aProp->setBoolValue("is-distance", _isDistance);
295 aProp->setDoubleValue("inbound-radial-deg", _bearing);
296 aProp->setDoubleValue("td", _holdTD);
299 /////////////////////////////////////////////////////////////////////////////
301 HeadingToAltitude::HeadingToAltitude(RouteBase* aOwner, const string& aIdent,
307 setFlag(WPT_DYNAMIC);
310 HeadingToAltitude::HeadingToAltitude(RouteBase* aOwner) :
315 void HeadingToAltitude::initFromProperties(SGPropertyNode_ptr aProp)
317 if (!aProp->hasChild("heading-deg")) {
318 throw sg_io_exception("missing heading/alt properties",
319 "HeadingToAltitude::initFromProperties");
322 Waypt::initFromProperties(aProp);
323 _magHeading = aProp->getDoubleValue("heading-deg");
324 _ident = aProp->getStringValue("ident");
327 void HeadingToAltitude::writeToProperties(SGPropertyNode_ptr aProp) const
329 Waypt::writeToProperties(aProp);
330 aProp->setStringValue("ident", _ident);
331 aProp->setDoubleValue("heading-deg", _magHeading);
334 /////////////////////////////////////////////////////////////////////////////
336 DMEIntercept::DMEIntercept(RouteBase* aOwner, const string& aIdent, const SGGeod& aPos,
337 double aCourseDeg, double aDistanceNm) :
341 _magCourse(aCourseDeg),
342 _dmeDistanceNm(aDistanceNm)
344 setFlag(WPT_DYNAMIC);
347 DMEIntercept::DMEIntercept(RouteBase* aOwner) :
352 void DMEIntercept::initFromProperties(SGPropertyNode_ptr aProp)
354 if (!aProp->hasChild("lon") || !aProp->hasChild("lat")) {
355 throw sg_io_exception("missing lon/lat properties",
356 "DMEIntercept::initFromProperties");
359 Waypt::initFromProperties(aProp);
360 _pos = SGGeod::fromDeg(aProp->getDoubleValue("lon"), aProp->getDoubleValue("lat"));
361 _ident = aProp->getStringValue("ident");
362 // check it's a real DME?
363 _magCourse = aProp->getDoubleValue("course-deg");
364 _dmeDistanceNm = aProp->getDoubleValue("dme-distance-nm");
368 void DMEIntercept::writeToProperties(SGPropertyNode_ptr aProp) const
370 Waypt::writeToProperties(aProp);
372 aProp->setStringValue("ident", _ident);
373 aProp->setDoubleValue("lon", _pos.getLongitudeDeg());
374 aProp->setDoubleValue("lat", _pos.getLatitudeDeg());
375 aProp->setDoubleValue("course-deg", _magCourse);
376 aProp->setDoubleValue("dme-distance-nm", _dmeDistanceNm);
379 /////////////////////////////////////////////////////////////////////////////
381 RadialIntercept::RadialIntercept(RouteBase* aOwner, const string& aIdent, const SGGeod& aPos,
382 double aCourseDeg, double aRadial) :
386 _magCourse(aCourseDeg),
389 setFlag(WPT_DYNAMIC);
392 RadialIntercept::RadialIntercept(RouteBase* aOwner) :
397 void RadialIntercept::initFromProperties(SGPropertyNode_ptr aProp)
399 if (!aProp->hasChild("lon") || !aProp->hasChild("lat")) {
400 throw sg_io_exception("missing lon/lat properties",
401 "RadialIntercept::initFromProperties");
404 Waypt::initFromProperties(aProp);
405 _pos = SGGeod::fromDeg(aProp->getDoubleValue("lon"), aProp->getDoubleValue("lat"));
406 _ident = aProp->getStringValue("ident");
407 // check it's a real VOR?
408 _magCourse = aProp->getDoubleValue("course-deg");
409 _radial = aProp->getDoubleValue("radial-deg");
413 void RadialIntercept::writeToProperties(SGPropertyNode_ptr aProp) const
415 Waypt::writeToProperties(aProp);
417 aProp->setStringValue("ident", _ident);
418 aProp->setDoubleValue("lon", _pos.getLongitudeDeg());
419 aProp->setDoubleValue("lat", _pos.getLatitudeDeg());
420 aProp->setDoubleValue("course-deg", _magCourse);
421 aProp->setDoubleValue("radial-deg", _radial);
424 /////////////////////////////////////////////////////////////////////////////
426 ATCVectors::ATCVectors(RouteBase* aOwner, FGAirport* aFacility) :
430 setFlag(WPT_DYNAMIC);
433 ATCVectors::~ATCVectors()
437 ATCVectors::ATCVectors(RouteBase* aOwner) :
442 SGGeod ATCVectors::position() const
444 return _facility->geod();
447 string ATCVectors::ident() const
449 return "VECTORS-" + _facility->ident();
452 void ATCVectors::initFromProperties(SGPropertyNode_ptr aProp)
454 if (!aProp->hasChild("icao")) {
455 throw sg_io_exception("missing icao propertie",
456 "ATCVectors::initFromProperties");
459 Waypt::initFromProperties(aProp);
460 _facility = FGAirport::getByIdent(aProp->getStringValue("icao"));
463 void ATCVectors::writeToProperties(SGPropertyNode_ptr aProp) const
465 Waypt::writeToProperties(aProp);
466 aProp->setStringValue("icao", _facility->ident());
469 /////////////////////////////////////////////////////////////////////////////
471 Discontinuity::Discontinuity(RouteBase* aOwner) :
474 setFlag(WPT_DYNAMIC);
475 setFlag(WPT_GENERATED); // prevent drag, delete, etc
478 Discontinuity::~Discontinuity()
482 SGGeod Discontinuity::position() const
484 return SGGeod(); // deliberately invalid of course
487 string Discontinuity::ident() const
489 return "DISCONTINUITY";
492 void Discontinuity::initFromProperties(SGPropertyNode_ptr aProp)
496 void Discontinuity::writeToProperties(SGPropertyNode_ptr aProp) const
498 Waypt::writeToProperties(aProp);
501 /////////////////////////////////////////////////////////////////////////////
503 SGGeod Via::position() const
508 string Via::ident() const
510 return "VIA " + _airway + " TO " + _to->ident();
513 Via::Via(RouteBase *aOwner) :
518 Via::Via(RouteBase *aOwner, const std::string &airwayName, FGPositioned *to) :
530 void Via::initFromProperties(SGPropertyNode_ptr aProp)
532 if (!aProp->hasChild("airway") || !aProp->hasChild("to")) {
533 throw sg_io_exception("missing airway/to propertie",
534 "Via::initFromProperties");
537 Waypt::initFromProperties(aProp);
539 _airway = aProp->getStringValue("airway");
540 Airway* way = Airway::findByIdent(_airway);
542 throw sg_io_exception("unknown airway idnet: '" + _airway + "'",
543 "Via::initFromProperties");
546 std::string idn(aProp->getStringValue("to"));
548 if (aProp->hasChild("lon")) {
549 p = SGGeod::fromDeg(aProp->getDoubleValue("lon"),
550 aProp->getDoubleValue("lat"));
553 FGPositionedRef nav = FGPositioned::findClosestWithIdent(idn, p, NULL);
555 throw sg_io_exception("unknown navaid ident:" + idn,
556 "Via::initFromProperties");
562 void Via::writeToProperties(SGPropertyNode_ptr aProp) const
564 Waypt::writeToProperties(aProp);
565 aProp->setStringValue("airway", _airway);
566 aProp->setStringValue("to", _to->ident());
567 // write lon/lat to disambiguate
568 aProp->setDoubleValue("lon", _to->geod().getLongitudeDeg());
569 aProp->setDoubleValue("lat", _to->geod().getLatitudeDeg());
572 WayptVec Via::expandToWaypoints(WayptRef aPreceeding) const
575 throw sg_exception("invalid preceeding waypoint");
578 Airway* way = Airway::findByIdent(_airway);
580 throw sg_exception("invalid airway");
583 return way->via(aPreceeding, new NavaidWaypoint(_to, owner()));