eaa147e3c2
Needed for realistic FMS route support.
586 lines
14 KiB
C++
586 lines
14 KiB
C++
// waypoint.cxx - waypoints that can occur in routes/procedures
|
|
// Written by James Turner, started 2009.
|
|
//
|
|
// Copyright (C) 2009 Curtis L. Olson
|
|
//
|
|
// This program is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU General Public License as
|
|
// published by the Free Software Foundation; either version 2 of the
|
|
// License, or (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful, but
|
|
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
// General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program; if not, write to the Free Software
|
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#include "waypoint.hxx"
|
|
|
|
#include <simgear/structure/exception.hxx>
|
|
|
|
#include <Airports/airport.hxx>
|
|
#include <Airports/runways.hxx>
|
|
#include <Navaids/airways.hxx>
|
|
|
|
using std::string;
|
|
|
|
namespace flightgear
|
|
{
|
|
|
|
BasicWaypt::BasicWaypt(const SGGeod& aPos, const string& aIdent, RouteBase* aOwner) :
|
|
Waypt(aOwner),
|
|
_pos(aPos),
|
|
_ident(aIdent)
|
|
{
|
|
}
|
|
|
|
BasicWaypt::BasicWaypt(RouteBase* aOwner) :
|
|
Waypt(aOwner)
|
|
{
|
|
}
|
|
|
|
void BasicWaypt::initFromProperties(SGPropertyNode_ptr aProp)
|
|
{
|
|
if (!aProp->hasChild("lon") || !aProp->hasChild("lat")) {
|
|
throw sg_io_exception("missing lon/lat properties",
|
|
"BasicWaypt::initFromProperties");
|
|
}
|
|
|
|
Waypt::initFromProperties(aProp);
|
|
|
|
_pos = SGGeod::fromDeg(aProp->getDoubleValue("lon"),
|
|
aProp->getDoubleValue("lat"));
|
|
_ident = aProp->getStringValue("ident");
|
|
}
|
|
|
|
void BasicWaypt::writeToProperties(SGPropertyNode_ptr aProp) const
|
|
{
|
|
Waypt::writeToProperties(aProp);
|
|
|
|
aProp->setStringValue("ident", _ident);
|
|
aProp->setDoubleValue("lon", _pos.getLongitudeDeg());
|
|
aProp->setDoubleValue("lat", _pos.getLatitudeDeg());
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
NavaidWaypoint::NavaidWaypoint(FGPositioned* aPos, RouteBase* aOwner) :
|
|
Waypt(aOwner),
|
|
_navaid(aPos)
|
|
{
|
|
if (aPos->type() == FGPositioned::RUNWAY) {
|
|
SG_LOG(SG_NAVAID, SG_WARN, "sure you don't want to be building a runway waypt here?");
|
|
}
|
|
}
|
|
|
|
NavaidWaypoint::NavaidWaypoint(RouteBase* aOwner) :
|
|
Waypt(aOwner)
|
|
{
|
|
}
|
|
|
|
|
|
SGGeod NavaidWaypoint::position() const
|
|
{
|
|
return SGGeod::fromGeodFt(_navaid->geod(), _altitudeFt);
|
|
}
|
|
|
|
std::string NavaidWaypoint::ident() const
|
|
{
|
|
return _navaid->ident();
|
|
}
|
|
|
|
void NavaidWaypoint::initFromProperties(SGPropertyNode_ptr aProp)
|
|
{
|
|
if (!aProp->hasChild("ident")) {
|
|
throw sg_io_exception("missing navaid value",
|
|
"NavaidWaypoint::initFromProperties");
|
|
}
|
|
|
|
Waypt::initFromProperties(aProp);
|
|
|
|
std::string idn(aProp->getStringValue("ident"));
|
|
SGGeod p;
|
|
if (aProp->hasChild("lon")) {
|
|
p = SGGeod::fromDeg(aProp->getDoubleValue("lon"), aProp->getDoubleValue("lat"));
|
|
}
|
|
|
|
// FIXME - resolve co-located DME, etc
|
|
// is it sufficent just to ignore DMEs, actually?
|
|
FGPositionedRef nav = FGPositioned::findClosestWithIdent(idn, p, NULL);
|
|
if (!nav) {
|
|
throw sg_io_exception("unknown navaid ident:" + idn,
|
|
"NavaidWaypoint::initFromProperties");
|
|
}
|
|
|
|
_navaid = nav;
|
|
}
|
|
|
|
void NavaidWaypoint::writeToProperties(SGPropertyNode_ptr aProp) const
|
|
{
|
|
Waypt::writeToProperties(aProp);
|
|
|
|
aProp->setStringValue("ident", _navaid->ident());
|
|
// write lon/lat to disambiguate
|
|
aProp->setDoubleValue("lon", _navaid->geod().getLongitudeDeg());
|
|
aProp->setDoubleValue("lat", _navaid->geod().getLatitudeDeg());
|
|
}
|
|
|
|
OffsetNavaidWaypoint::OffsetNavaidWaypoint(FGPositioned* aPos, RouteBase* aOwner,
|
|
double aRadial, double aDistNm) :
|
|
NavaidWaypoint(aPos, aOwner),
|
|
_radial(aRadial),
|
|
_distanceNm(aDistNm)
|
|
{
|
|
init();
|
|
}
|
|
|
|
OffsetNavaidWaypoint::OffsetNavaidWaypoint(RouteBase* aOwner) :
|
|
NavaidWaypoint(aOwner)
|
|
{
|
|
}
|
|
|
|
void OffsetNavaidWaypoint::init()
|
|
{
|
|
SGGeod offset;
|
|
double az2;
|
|
SGGeodesy::direct(_navaid->geod(), _radial, _distanceNm * SG_NM_TO_METER, offset, az2);
|
|
_geod = SGGeod::fromGeodFt(offset, _altitudeFt);
|
|
}
|
|
|
|
void OffsetNavaidWaypoint::initFromProperties(SGPropertyNode_ptr aProp)
|
|
{
|
|
if (!aProp->hasChild("radial-deg") || !aProp->hasChild("distance-nm")) {
|
|
throw sg_io_exception("missing radial/offset distance",
|
|
"OffsetNavaidWaypoint::initFromProperties");
|
|
}
|
|
|
|
NavaidWaypoint::initFromProperties(aProp);
|
|
_radial = aProp->getDoubleValue("radial-deg");
|
|
_distanceNm = aProp->getDoubleValue("distance-nm");
|
|
init();
|
|
}
|
|
|
|
void OffsetNavaidWaypoint::writeToProperties(SGPropertyNode_ptr aProp) const
|
|
{
|
|
NavaidWaypoint::writeToProperties(aProp);
|
|
aProp->setDoubleValue("radial-deg", _radial);
|
|
aProp->setDoubleValue("distance-nm", _distanceNm);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
RunwayWaypt::RunwayWaypt(FGRunway* aPos, RouteBase* aOwner) :
|
|
Waypt(aOwner),
|
|
_runway(aPos)
|
|
{
|
|
}
|
|
|
|
RunwayWaypt::RunwayWaypt(RouteBase* aOwner) :
|
|
Waypt(aOwner)
|
|
{
|
|
}
|
|
|
|
SGGeod RunwayWaypt::position() const
|
|
{
|
|
return _runway->threshold();
|
|
}
|
|
|
|
std::string RunwayWaypt::ident() const
|
|
{
|
|
return _runway->airport()->ident() + "-" + _runway->ident();
|
|
}
|
|
|
|
FGPositioned* RunwayWaypt::source() const
|
|
{
|
|
return _runway;
|
|
}
|
|
|
|
double RunwayWaypt::headingRadialDeg() const
|
|
{
|
|
return _runway->headingDeg();
|
|
}
|
|
|
|
void RunwayWaypt::initFromProperties(SGPropertyNode_ptr aProp)
|
|
{
|
|
if (!aProp->hasChild("icao") || !aProp->hasChild("ident")) {
|
|
throw sg_io_exception("missing values: icao or ident",
|
|
"RunwayWaypoint::initFromProperties");
|
|
}
|
|
|
|
Waypt::initFromProperties(aProp);
|
|
std::string idn(aProp->getStringValue("ident"));
|
|
const FGAirport* apt = FGAirport::getByIdent(aProp->getStringValue("icao"));
|
|
_runway = apt->getRunwayByIdent(aProp->getStringValue("ident"));
|
|
}
|
|
|
|
void RunwayWaypt::writeToProperties(SGPropertyNode_ptr aProp) const
|
|
{
|
|
Waypt::writeToProperties(aProp);
|
|
aProp->setStringValue("ident", _runway->ident());
|
|
aProp->setStringValue("icao", _runway->airport()->ident());
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
Hold::Hold(const SGGeod& aPos, const string& aIdent, RouteBase* aOwner) :
|
|
BasicWaypt(aPos, aIdent, aOwner),
|
|
_righthanded(true),
|
|
_isDistance(false)
|
|
{
|
|
setFlag(WPT_DYNAMIC);
|
|
}
|
|
|
|
Hold::Hold(RouteBase* aOwner) :
|
|
BasicWaypt(aOwner),
|
|
_righthanded(true),
|
|
_isDistance(false)
|
|
{
|
|
}
|
|
|
|
void Hold::setHoldRadial(double aInboundRadial)
|
|
{
|
|
_bearing = aInboundRadial;
|
|
}
|
|
|
|
void Hold::setHoldDistance(double aDistanceNm)
|
|
{
|
|
_isDistance = true;
|
|
_holdTD = aDistanceNm;
|
|
}
|
|
|
|
void Hold::setHoldTime(double aTimeSec)
|
|
{
|
|
_isDistance = false;
|
|
_holdTD = aTimeSec;
|
|
}
|
|
|
|
void Hold::setRightHanded()
|
|
{
|
|
_righthanded = true;
|
|
}
|
|
|
|
void Hold::setLeftHanded()
|
|
{
|
|
_righthanded = false;
|
|
}
|
|
|
|
void Hold::initFromProperties(SGPropertyNode_ptr aProp)
|
|
{
|
|
BasicWaypt::initFromProperties(aProp);
|
|
|
|
if (!aProp->hasChild("lon") || !aProp->hasChild("lat")) {
|
|
throw sg_io_exception("missing lon/lat properties",
|
|
"Hold::initFromProperties");
|
|
}
|
|
|
|
_righthanded = aProp->getBoolValue("right-handed");
|
|
_isDistance = aProp->getBoolValue("is-distance");
|
|
_bearing = aProp->getDoubleValue("inbound-radial-deg");
|
|
_holdTD = aProp->getDoubleValue("td");
|
|
}
|
|
|
|
void Hold::writeToProperties(SGPropertyNode_ptr aProp) const
|
|
{
|
|
BasicWaypt::writeToProperties(aProp);
|
|
|
|
aProp->setBoolValue("right-handed", _righthanded);
|
|
aProp->setBoolValue("is-distance", _isDistance);
|
|
aProp->setDoubleValue("inbound-radial-deg", _bearing);
|
|
aProp->setDoubleValue("td", _holdTD);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
HeadingToAltitude::HeadingToAltitude(RouteBase* aOwner, const string& aIdent,
|
|
double aMagHdg) :
|
|
Waypt(aOwner),
|
|
_ident(aIdent),
|
|
_magHeading(aMagHdg)
|
|
{
|
|
setFlag(WPT_DYNAMIC);
|
|
}
|
|
|
|
HeadingToAltitude::HeadingToAltitude(RouteBase* aOwner) :
|
|
Waypt(aOwner)
|
|
{
|
|
}
|
|
|
|
void HeadingToAltitude::initFromProperties(SGPropertyNode_ptr aProp)
|
|
{
|
|
if (!aProp->hasChild("heading-deg")) {
|
|
throw sg_io_exception("missing heading/alt properties",
|
|
"HeadingToAltitude::initFromProperties");
|
|
}
|
|
|
|
Waypt::initFromProperties(aProp);
|
|
_magHeading = aProp->getDoubleValue("heading-deg");
|
|
_ident = aProp->getStringValue("ident");
|
|
}
|
|
|
|
void HeadingToAltitude::writeToProperties(SGPropertyNode_ptr aProp) const
|
|
{
|
|
Waypt::writeToProperties(aProp);
|
|
aProp->setStringValue("ident", _ident);
|
|
aProp->setDoubleValue("heading-deg", _magHeading);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
DMEIntercept::DMEIntercept(RouteBase* aOwner, const string& aIdent, const SGGeod& aPos,
|
|
double aCourseDeg, double aDistanceNm) :
|
|
Waypt(aOwner),
|
|
_ident(aIdent),
|
|
_pos(aPos),
|
|
_magCourse(aCourseDeg),
|
|
_dmeDistanceNm(aDistanceNm)
|
|
{
|
|
setFlag(WPT_DYNAMIC);
|
|
}
|
|
|
|
DMEIntercept::DMEIntercept(RouteBase* aOwner) :
|
|
Waypt(aOwner)
|
|
{
|
|
}
|
|
|
|
void DMEIntercept::initFromProperties(SGPropertyNode_ptr aProp)
|
|
{
|
|
if (!aProp->hasChild("lon") || !aProp->hasChild("lat")) {
|
|
throw sg_io_exception("missing lon/lat properties",
|
|
"DMEIntercept::initFromProperties");
|
|
}
|
|
|
|
Waypt::initFromProperties(aProp);
|
|
_pos = SGGeod::fromDeg(aProp->getDoubleValue("lon"), aProp->getDoubleValue("lat"));
|
|
_ident = aProp->getStringValue("ident");
|
|
// check it's a real DME?
|
|
_magCourse = aProp->getDoubleValue("course-deg");
|
|
_dmeDistanceNm = aProp->getDoubleValue("dme-distance-nm");
|
|
|
|
}
|
|
|
|
void DMEIntercept::writeToProperties(SGPropertyNode_ptr aProp) const
|
|
{
|
|
Waypt::writeToProperties(aProp);
|
|
|
|
aProp->setStringValue("ident", _ident);
|
|
aProp->setDoubleValue("lon", _pos.getLongitudeDeg());
|
|
aProp->setDoubleValue("lat", _pos.getLatitudeDeg());
|
|
aProp->setDoubleValue("course-deg", _magCourse);
|
|
aProp->setDoubleValue("dme-distance-nm", _dmeDistanceNm);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
RadialIntercept::RadialIntercept(RouteBase* aOwner, const string& aIdent, const SGGeod& aPos,
|
|
double aCourseDeg, double aRadial) :
|
|
Waypt(aOwner),
|
|
_ident(aIdent),
|
|
_pos(aPos),
|
|
_magCourse(aCourseDeg),
|
|
_radial(aRadial)
|
|
{
|
|
setFlag(WPT_DYNAMIC);
|
|
}
|
|
|
|
RadialIntercept::RadialIntercept(RouteBase* aOwner) :
|
|
Waypt(aOwner)
|
|
{
|
|
}
|
|
|
|
void RadialIntercept::initFromProperties(SGPropertyNode_ptr aProp)
|
|
{
|
|
if (!aProp->hasChild("lon") || !aProp->hasChild("lat")) {
|
|
throw sg_io_exception("missing lon/lat properties",
|
|
"RadialIntercept::initFromProperties");
|
|
}
|
|
|
|
Waypt::initFromProperties(aProp);
|
|
_pos = SGGeod::fromDeg(aProp->getDoubleValue("lon"), aProp->getDoubleValue("lat"));
|
|
_ident = aProp->getStringValue("ident");
|
|
// check it's a real VOR?
|
|
_magCourse = aProp->getDoubleValue("course-deg");
|
|
_radial = aProp->getDoubleValue("radial-deg");
|
|
|
|
}
|
|
|
|
void RadialIntercept::writeToProperties(SGPropertyNode_ptr aProp) const
|
|
{
|
|
Waypt::writeToProperties(aProp);
|
|
|
|
aProp->setStringValue("ident", _ident);
|
|
aProp->setDoubleValue("lon", _pos.getLongitudeDeg());
|
|
aProp->setDoubleValue("lat", _pos.getLatitudeDeg());
|
|
aProp->setDoubleValue("course-deg", _magCourse);
|
|
aProp->setDoubleValue("radial-deg", _radial);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
ATCVectors::ATCVectors(RouteBase* aOwner, FGAirport* aFacility) :
|
|
Waypt(aOwner),
|
|
_facility(aFacility)
|
|
{
|
|
setFlag(WPT_DYNAMIC);
|
|
}
|
|
|
|
ATCVectors::~ATCVectors()
|
|
{
|
|
}
|
|
|
|
ATCVectors::ATCVectors(RouteBase* aOwner) :
|
|
Waypt(aOwner)
|
|
{
|
|
}
|
|
|
|
SGGeod ATCVectors::position() const
|
|
{
|
|
return _facility->geod();
|
|
}
|
|
|
|
string ATCVectors::ident() const
|
|
{
|
|
return "VECTORS-" + _facility->ident();
|
|
}
|
|
|
|
void ATCVectors::initFromProperties(SGPropertyNode_ptr aProp)
|
|
{
|
|
if (!aProp->hasChild("icao")) {
|
|
throw sg_io_exception("missing icao propertie",
|
|
"ATCVectors::initFromProperties");
|
|
}
|
|
|
|
Waypt::initFromProperties(aProp);
|
|
_facility = FGAirport::getByIdent(aProp->getStringValue("icao"));
|
|
}
|
|
|
|
void ATCVectors::writeToProperties(SGPropertyNode_ptr aProp) const
|
|
{
|
|
Waypt::writeToProperties(aProp);
|
|
aProp->setStringValue("icao", _facility->ident());
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
Discontinuity::Discontinuity(RouteBase* aOwner) :
|
|
Waypt(aOwner)
|
|
{
|
|
setFlag(WPT_DYNAMIC);
|
|
setFlag(WPT_GENERATED); // prevent drag, delete, etc
|
|
}
|
|
|
|
Discontinuity::~Discontinuity()
|
|
{
|
|
}
|
|
|
|
SGGeod Discontinuity::position() const
|
|
{
|
|
return SGGeod(); // deliberately invalid of course
|
|
}
|
|
|
|
string Discontinuity::ident() const
|
|
{
|
|
return "DISCONTINUITY";
|
|
}
|
|
|
|
void Discontinuity::initFromProperties(SGPropertyNode_ptr aProp)
|
|
{
|
|
}
|
|
|
|
void Discontinuity::writeToProperties(SGPropertyNode_ptr aProp) const
|
|
{
|
|
Waypt::writeToProperties(aProp);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
SGGeod Via::position() const
|
|
{
|
|
return _to->geod();
|
|
}
|
|
|
|
string Via::ident() const
|
|
{
|
|
return "VIA " + _airway + " TO " + _to->ident();
|
|
}
|
|
|
|
Via::Via(RouteBase *aOwner) :
|
|
Waypt(aOwner)
|
|
{
|
|
}
|
|
|
|
Via::Via(RouteBase *aOwner, const std::string &airwayName, FGPositioned *to) :
|
|
Waypt(aOwner),
|
|
_airway(airwayName),
|
|
_to(to)
|
|
{
|
|
}
|
|
|
|
Via::~Via()
|
|
{
|
|
|
|
}
|
|
|
|
void Via::initFromProperties(SGPropertyNode_ptr aProp)
|
|
{
|
|
if (!aProp->hasChild("airway") || !aProp->hasChild("to")) {
|
|
throw sg_io_exception("missing airway/to propertie",
|
|
"Via::initFromProperties");
|
|
}
|
|
|
|
Waypt::initFromProperties(aProp);
|
|
|
|
_airway = aProp->getStringValue("airway");
|
|
Airway* way = Airway::findByIdent(_airway);
|
|
if (!way) {
|
|
throw sg_io_exception("unknown airway idnet: '" + _airway + "'",
|
|
"Via::initFromProperties");
|
|
}
|
|
|
|
std::string idn(aProp->getStringValue("to"));
|
|
SGGeod p;
|
|
if (aProp->hasChild("lon")) {
|
|
p = SGGeod::fromDeg(aProp->getDoubleValue("lon"),
|
|
aProp->getDoubleValue("lat"));
|
|
}
|
|
|
|
FGPositionedRef nav = FGPositioned::findClosestWithIdent(idn, p, NULL);
|
|
if (!nav) {
|
|
throw sg_io_exception("unknown navaid ident:" + idn,
|
|
"Via::initFromProperties");
|
|
}
|
|
|
|
_to = nav;
|
|
}
|
|
|
|
void Via::writeToProperties(SGPropertyNode_ptr aProp) const
|
|
{
|
|
Waypt::writeToProperties(aProp);
|
|
aProp->setStringValue("airway", _airway);
|
|
aProp->setStringValue("to", _to->ident());
|
|
// write lon/lat to disambiguate
|
|
aProp->setDoubleValue("lon", _to->geod().getLongitudeDeg());
|
|
aProp->setDoubleValue("lat", _to->geod().getLatitudeDeg());
|
|
}
|
|
|
|
WayptVec Via::expandToWaypoints(WayptRef aPreceeding) const
|
|
{
|
|
if (!aPreceeding) {
|
|
throw sg_exception("invalid preceeding waypoint");
|
|
}
|
|
|
|
Airway* way = Airway::findByIdent(_airway);
|
|
if (!way) {
|
|
throw sg_exception("invalid airway");
|
|
}
|
|
|
|
return way->via(aPreceeding, new NavaidWaypoint(_to, owner()));
|
|
}
|
|
|
|
} // of namespace
|