28#include <simgear/sg_inlines.h>
29#include <simgear/structure/exception.hxx>
30#include <simgear/io/iostreams/sgstream.hxx>
31#include <simgear/misc/sg_path.hxx>
95 static Network* static_lowLevel =
nullptr;
97 if (!static_lowLevel) {
102 return static_lowLevel;
107 static Network* static_highLevel =
nullptr;
108 if (!static_highLevel) {
109 static_highLevel =
new Network;
113 return static_highLevel;
116Airway::Airway(
const std::string& aIdent,
119 int aTop,
int aBottom) :
123 _topAltitudeFt(aTop),
124 _bottomAltitudeFt(aBottom)
132 std::string identStart, identEnd,
name;
133 double latStart, lonStart, latEnd, lonEnd;
136 sg_gzifstream in( path );
137 if ( !in.is_open() ) {
138 SG_LOG( SG_NAVAID, SG_ALERT,
"Cannot open file: " << path );
139 throw sg_io_exception(
"Could not open airways data", path);
149 if (identStart ==
"99") {
153 in >> latStart >> lonStart >> identEnd >> latEnd >> lonEnd >> type >> base >> top >>
name;
160 SGGeod startPos(SGGeod::fromDeg(lonStart, latStart)),
161 endPos(SGGeod::fromDeg(lonEnd, latEnd));
165 }
else if (type == 2) {
168 SG_LOG(SG_NAVAID, SG_DEV_WARN,
"unknown airway type:" << type <<
" for " <<
name);
172 auto pieces = simgear::strutils::split(
name,
"-");
173 for (
auto p : pieces) {
174 int awy = net->findAirway(
p);
175 net->addEdge(awy, startPos, identStart, endPos, identEnd);
180WayptVec::const_iterator Airway::find(
WayptRef wpt)
const
182 assert(!_elements.empty());
183 if (wpt->type() ==
"via") {
189 return std::find_if(_elements.begin(), _elements.end(),
192 if (!w) return false;
193 return w->matches(wpt);
201 auto fit = find(from);
204 if ((fit == _elements.end()) || (tit == _elements.end())) {
210 for (++fit; fit != tit; ++fit) {
211 if (*fit ==
nullptr) {
218 for (--fit; fit != tit; --fit) {
219 if (*fit ==
nullptr) {
234 auto fit = find(from);
237 if ((fit == _elements.end()) || (tit == _elements.end())) {
238 throw sg_exception(
"bad VIA transition points");
252 for (++fit; fit != tit; ++fit) {
257 for (--fit; fit != tit; --fit) {
272 auto it = std::find_if(_elements.begin(), _elements.end(),
275 if (!w) return false;
276 return w->matches(navaid);
278 return (it != _elements.end());
281int Airway::Network::findAirway(
const std::string& aName)
286 { return (awy->_level == level) && (awy->ident() == aName); });
288 return (*it)->_cacheId;
299 if ((level != Both) && (awy->_level != level)) return false;
300 return (awy->ident() == aIdent);
309 airwayId = ndc->findAirway(
HighLevel, aIdent,
false);
318 airwayId = ndc->findAirway(
level, aIdent,
false);
324 return ndc->loadAirway(airwayId);
331 { return (awy->_cacheId == cacheId); });
340void Airway::loadWaypoints()
const
345 _elements.push_back({});
348 auto wp =
new NavaidWaypoint(pos,
const_cast<Airway*
>(
this));
351 _elements.push_back(wp);
359 if (hi && hi->canVia(from, to)) {
364 if (low && low->canVia(from, to)) {
374 if (hi && hi->containsNavaid(nav)) {
379 if (low && low->containsNavaid(nav)) {
389 auto it = std::find_if(_elements.begin(), _elements.end(),
392 if (!w) return false;
393 return w->ident() == aIdent;
396 if (it != _elements.end())
404 auto it = std::find_if(_elements.begin(), _elements.end(),
407 if (!w) return false;
408 return w->source() == nav;
411 if (it != _elements.end())
416void Airway::Network::addEdge(
int aWay,
const SGGeod& aStartPos,
417 const std::string& aStartIdent,
418 const SGGeod& aEndPos,
const std::string& aEndIdent)
424 SG_LOG(SG_NAVAID, SG_DEBUG,
"unknown airways start pt: '" << aStartIdent <<
"'");
429 SG_LOG(SG_NAVAID, SG_DEBUG,
"unknown airways end pt: '" << aEndIdent <<
"'");
440 double rawDiff = b - a;
441 SG_NORMALIZE_RANGE(rawDiff, -180.0, 180.0);
445bool Airway::Network::inNetwork(
PositionedID posID)
const
447 NetworkMembershipDict::iterator it = _inNetworkCache.find(posID);
448 if (it != _inNetworkCache.end()) {
453 _inNetworkCache.insert(it, std::make_pair(posID, r));
460 if (!aFrom || !aTo) {
461 throw sg_exception(
"invalid waypoints to route between");
469 bool exactTo, exactFrom;
473#ifdef DEBUG_AWY_SEARCH
474 SG_LOG(SG_NAVAID, SG_INFO,
"from:" << from->ident() <<
"/" << from->name());
475 SG_LOG(SG_NAVAID, SG_INFO,
"to:" << to->ident() <<
"/" << to->name());
478 bool ok = search2(from, to, aPath);
483 return cleanGeneratedPath(aFrom, aTo, aPath, exactTo, exactFrom);
487 bool exactTo,
bool exactFrom)
501 const double MAX_DOG_LEG = 90.0;
502 double enrouteCourse = SGGeodesy::courseDeg(aFrom->position(), aTo->position()),
503 finalLegCourse = SGGeodesy::courseDeg(aPath.back()->position(), aTo->position());
505 bool isDogLeg = fabs(
headingDiffDeg(enrouteCourse, finalLegCourse)) > MAX_DOG_LEG;
506 if (exactTo || isDogLeg) {
517 double initialLegCourse = SGGeodesy::courseDeg(aFrom->position(), aPath.front()->position());
518 isDogLeg = fabs(
headingDiffDeg(enrouteCourse, initialLegCourse)) > MAX_DOG_LEG;
519 if (exactFrom || isDogLeg) {
520 aPath.erase(aPath.begin());
526std::pair<FGPositionedRef, bool>
529 if (aRef->source()) {
531 if (inNetwork(aRef->source()->guid())) {
532 return std::make_pair(aRef->source(),
true);
536 return findClosestNode(aRef->position());
548 return _net->inNetwork(aPos->
guid());
561std::pair<FGPositionedRef, bool>
568 if (r && (SGGeodesy::distanceM(aGeod, r->geod()) < 100.0)) {
572 return make_pair(r, exact);
591 for (; n !=
nullptr; ++count, n = n->previous) {;}
592 aRoute.resize(count);
595 for (n = aNode; n; n=n->previous) {
603 aRoute[--count] = wp;
613 for (
unsigned int i=0;
i<aHeap.size(); ++
i) {
614 if (aHeap[
i]->node == aPos) {
634 typedef set<PositionedID> ClosedNodeSet;
637 ClosedNodeSet closedNodes;
640 openNodes.push_back(
new AStarOpenNode(aStart, 0.0, 0, aDest,
nullptr));
643 while (!openNodes.empty()) {
644 std::pop_heap(openNodes.begin(), openNodes.end(), ordering);
647 openNodes.pop_back();
648 closedNodes.insert(xp->
guid());
650#ifdef DEBUG_AWY_SEARCH
651 SG_LOG(SG_NAVAID, SG_INFO,
"x:" << xp->
ident() <<
", f(x)=" << x->totalCost());
663 for (
auto other : cache->airwayEdgesFrom(_networkID, xp->
guid())) {
664 if (closedNodes.count(other.second)) {
668 FGPositioned* yp = cache->loadById(other.second);
669 double edgeDistanceM = SGGeodesy::distanceM(xp->
geod(), yp->
geod());
672 double g = x->distanceFromStart + edgeDistanceM;
673 if (g > y->distanceFromStart) {
675#ifdef DEBUG_AWY_SEARCH
676 SG_LOG(SG_NAVAID, SG_INFO,
"\tabandoning " << yp->
ident() <<
677 " path is worse: g(y)" << y->distanceFromStart <<
", g'=" << g);
684#ifdef DEBUG_AWY_SEARCH
685 SG_LOG(SG_NAVAID, SG_INFO,
"\tfixing up previous for new path to " << yp->
ident() <<
", d =" << g);
688 y->distanceFromStart =
g;
689 y->airway = other.first;
690 std::make_heap(openNodes.begin(), openNodes.end(), ordering);
692 y =
new AStarOpenNode(yp, edgeDistanceM, other.first, aDest, x);
693#ifdef DEBUG_AWY_SEARCH
694 SG_LOG(SG_NAVAID, SG_INFO,
"\ty=" << yp->
ident() <<
", f(y)=" << y->totalCost());
696 openNodes.push_back(y);
697 std::push_heap(openNodes.begin(), openNodes.end(), ordering);
702 SG_LOG(SG_NAVAID, SG_INFO,
"A* failed to find route");
SGSharedPtr< FGPositioned > FGPositionedRef
Predicate class to support custom filtering of FGPositioned queries Default implementation of this pa...
static FGPositionedRef createWaypoint(FGPositioned::Type aType, const std::string &aIdent, const SGGeod &aPos, bool isTemporary=false, const std::string &aName={})
PositionedID guid() const
static FGPositionedRef findClosestWithIdent(const std::string &aIdent, const SGGeod &aPos, Filter *aFilter=NULL)
virtual const SGGeod & geod() const
const std::string & ident() const
static FGPositionedRef findClosest(const SGGeod &aPos, double aCutoffNm, Filter *aFilter=NULL)
Find the closest item to a position, which pass the specified filter A cutoff range in NM must be spe...
SGSharedPtr< AStarOpenNode > previous
AStarOpenNode(FGPositionedRef aNode, double aLegDist, int aAirway, FGPositionedRef aDest, AStarOpenNode *aPrev)
double directDistanceToDestination
double totalCost() const
aka 'f(x)'
Track a network of airways.
std::pair< FGPositionedRef, bool > findClosestNode(const SGGeod &aGeod)
Overloaded version working with a raw SGGeod.
FGPositionedRef findNodeByIdent(const std::string &ident, const SGGeod &near) const
friend class InAirwayFilter
bool route(WayptRef aFrom, WayptRef aTo, WayptVec &aPath)
Principal routing algorithm.
static Network * lowLevel()
WayptVec via(const WayptRef &from, const WayptRef &to) const
@ HighLevel
Victor airways.
bool containsNavaid(const FGPositionedRef &navaid) const
WayptRef findEnroute(const std::string &aIdent) const
bool canVia(const WayptRef &from, const WayptRef &to) const
static void loadAWYDat(const SGPath &path)
static AirwayRef findByIdentAndNavaid(const std::string &aIdent, const FGPositionedRef nav)
Find an airway by ident, and containing a particula rnavaid/fix.
static Network * highLevel()
std::string ident() const override
static AirwayRef findByIdent(const std::string &aIdent, Level level)
friend class NavDataCache
static AirwayRef loadByCacheId(int cacheId)
static AirwayRef findByIdentAndVia(const std::string &aIdent, const WayptRef &from, const WayptRef &to)
Find the airway based on its ident.
bool operator()(AStarOpenNode *a, AStarOpenNode *b)
virtual FGPositioned::Type minType() const
virtual FGPositioned::Type maxType() const
virtual bool pass(FGPositioned *aPos) const
Over-rideable filter method.
InAirwayFilter(const Airway::Network *aNet)
PositionedIDVec airwayWaypts(int id)
Waypoints on the airway.
AirwayRef loadAirway(int airwayID)
FGPositionedRef loadById(PositionedID guid)
retrieve an FGPositioned from the cache.
static NavDataCache * instance()
void insertEdge(int network, int airwayID, PositionedID from, PositionedID to)
insert an edge between two positioned nodes, into the network.
bool isInAirwayNetwork(int network, PositionedID pos)
is the specified positioned a node on the network?
int findAirway(int network, const std::string &aName, bool create)
Waypoint based upon a navaid.
FlightPlan.hxx - defines a full flight-plan object, including departure, cruise, arrival information ...
SGSharedPtr< AStarOpenNode > AStarOpenNodeRef
vector< AStarOpenNodeRef > OpenNodeHeap
static double headingDiffDeg(double a, double b)
SGSharedPtr< FGPositioned > FGPositionedRef
SGSharedPtr< Waypt > WayptRef
SGSharedPtr< Airway > AirwayRef
@ WPT_GENERATED
waypoint was created automatically (not manually entered/loaded) for example waypoints from airway ro...
@ WPT_VIA
waypoint prodcued by expanding a VIA segment
static void buildWaypoints(AStarOpenNodeRef aNode, WayptVec &aRoute)
std::vector< WayptRef > WayptVec
static std::vector< AirwayRef > static_airwaysCache
static AStarOpenNodeRef findInOpen(const OpenNodeHeap &aHeap, FGPositioned *aPos)
Inefficent (linear) helper to find an open node in the heap.