From eaa147e3c2a5f0975518910e7cbec7c665ce9753 Mon Sep 17 00:00:00 2001 From: James Turner Date: Thu, 13 Nov 2014 21:03:24 +0000 Subject: [PATCH] Work on FlightPlan VIA / discontinuity support. Needed for realistic FMS route support. --- src/Navaids/FlightPlan.cxx | 254 ++++++++++----- src/Navaids/FlightPlan.hxx | 132 ++++---- src/Navaids/NavDataCache.cxx | 607 +++++++++++++++++++---------------- src/Navaids/NavDataCache.hxx | 98 +++--- src/Navaids/airways.cxx | 95 ++++++ src/Navaids/airways.hxx | 16 +- src/Navaids/route.cxx | 6 +- src/Navaids/waypoint.cxx | 118 +++++++ src/Navaids/waypoint.hxx | 50 ++- 9 files changed, 905 insertions(+), 471 deletions(-) diff --git a/src/Navaids/FlightPlan.cxx b/src/Navaids/FlightPlan.cxx index 339ae0a19..89e1f5f41 100644 --- a/src/Navaids/FlightPlan.cxx +++ b/src/Navaids/FlightPlan.cxx @@ -50,6 +50,7 @@ #include #include #include +#include using std::string; using std::vector; @@ -334,20 +335,6 @@ void FlightPlan::setCurrentIndex(int index) unlockDelegate(); } -void FlightPlan::activate() -{ - lockDelegate(); - - _currentIndex = 0; - _currentWaypointChanged = true; - - if (_delegate) { - _delegate->runActivated(); - } - - unlockDelegate(); -} - void FlightPlan::finish() { if (_currentIndex == -1) { @@ -976,11 +963,88 @@ double FlightPlan::magvarDegAt(const SGGeod& pos) const return sgGetMagVar(pos, jd) * SG_RADIANS_TO_DEGREES; } + +namespace +{ + +WayptRef intersectionFromString(FGPositionedRef p1, + const SGGeod& basePosition, + const double magvar, + const string_list& pieces) +{ + assert(pieces.size() == 4); + // navid/radial/navid/radial notation + FGPositionedRef p2 = FGPositioned::findClosestWithIdent(pieces[2], basePosition); + if (!p2) { + SG_LOG( SG_NAVAID, SG_INFO, "Unable to find FGPositioned with ident:" << pieces[2]); + return NULL; + } + + double r1 = atof(pieces[1].c_str()), + r2 = atof(pieces[3].c_str()); + r1 += magvar; + r2 += magvar; + + SGGeod intersection; + bool ok = SGGeodesy::radialIntersection(p1->geod(), r1, p2->geod(), r2, intersection); + if (!ok) { + SG_LOG(SG_NAVAID, SG_INFO, "no valid intersection for:" << pieces[0] + << "/" << pieces[2]); + return NULL; + } + + std::string name = p1->ident() + "-" + p2->ident(); + return new BasicWaypt(intersection, name, NULL); +} + +WayptRef wayptFromLonLatString(const std::string& target) +{ + size_t pos = target.find( ',' ); + if ( pos == string::npos ) + return WayptRef(); + + double lon = atof( target.substr(0, pos).c_str()); + double lat = atof( target.c_str() + pos + 1); + char buf[32]; + char ew = (lon < 0.0) ? 'W' : 'E'; + char ns = (lat < 0.0) ? 'S' : 'N'; + snprintf(buf, 32, "%c%03d%c%03d", ew, (int) fabs(lon), ns, (int)fabs(lat)); + + return new BasicWaypt(SGGeod::fromDeg(lon, lat), buf, NULL); +} + +WayptRef viaFromString(const SGGeod& basePosition, const std::string& target) +{ + assert(target.find("VIA ") == 0); + string_list pieces(simgear::strutils::split(target, "/")); + if ((pieces.size() != 4) || (pieces[2] != "TO")) { + SG_LOG( SG_NAVAID, SG_WARN, "Malformed VIA specification string:" << target); + return NULL; + } + + // airway ident is pieces[1] + Airway* airway = Airway::findByIdent(pieces[1]); + if (airway == NULL) { + SG_LOG( SG_NAVAID, SG_WARN, "Unknown airway:" << pieces[1]); + return NULL; + } + + // TO navaid is pieces[3] + FGPositionedRef nav = FGPositioned::findClosestWithIdent(pieces[3], basePosition, NULL); + if (!nav || !airway->containsNavaid(nav)) { + SG_LOG( SG_NAVAID, SG_WARN, "TO navaid:" << pieces[3] << " unknown or not on airway"); + return NULL; + } + + Via* via = new Via(NULL, pieces[1], nav); + return via; +} + +} // of anonymous namespace + WayptRef FlightPlan::waypointFromString(const string& tgt ) { - string target(boost::to_upper_copy(tgt)); - WayptRef wpt; - + string target(boost::to_upper_copy(tgt)); // extract altitude double altFt = 0.0; RouteRestriction altSetting = RESTRICT_NONE; @@ -995,22 +1059,8 @@ WayptRef FlightPlan::waypointFromString(const string& tgt ) } // check for lon,lat - pos = target.find( ',' ); - if ( pos != string::npos ) { - double lon = atof( target.substr(0, pos).c_str()); - double lat = atof( target.c_str() + pos + 1); - char buf[32]; - char ew = (lon < 0.0) ? 'W' : 'E'; - char ns = (lat < 0.0) ? 'S' : 'N'; - snprintf(buf, 32, "%c%03d%c%03d", ew, (int) fabs(lon), ns, (int)fabs(lat)); - - wpt = new BasicWaypt(SGGeod::fromDeg(lon, lat), buf, NULL); - if (altSetting != RESTRICT_NONE) { - wpt->setAltitude(altFt, altSetting); - } - return wpt; - } - + WayptRef wpt = wayptFromLonLatString(target); + SGGeod basePosition; if (_legs.empty()) { // route is empty, use current position @@ -1019,59 +1069,45 @@ WayptRef FlightPlan::waypointFromString(const string& tgt ) basePosition = _legs.back()->waypoint()->position(); } - string_list pieces(simgear::strutils::split(target, "/")); - FGPositionedRef p = FGPositioned::findClosestWithIdent(pieces.front(), basePosition); - if (!p) { - SG_LOG( SG_NAVAID, SG_INFO, "Unable to find FGPositioned with ident:" << pieces.front()); - return NULL; - } - - double magvar = magvarDegAt(basePosition); - - if (pieces.size() == 1) { - wpt = new NavaidWaypoint(p, NULL); - } else if (pieces.size() == 3) { - // navaid/radial/distance-nm notation - double radial = atof(pieces[1].c_str()), - distanceNm = atof(pieces[2].c_str()); - radial += magvar; - wpt = new OffsetNavaidWaypoint(p, NULL, radial, distanceNm); - } else if (pieces.size() == 2) { - FGAirport* apt = dynamic_cast(p.ptr()); - if (!apt) { - SG_LOG(SG_NAVAID, SG_INFO, "Waypoint is not an airport:" << pieces.front()); - return NULL; - } - - if (!apt->hasRunwayWithIdent(pieces[1])) { - SG_LOG(SG_NAVAID, SG_INFO, "No runway: " << pieces[1] << " at " << pieces[0]); - return NULL; - } - - FGRunway* runway = apt->getRunwayByIdent(pieces[1]); - wpt = new NavaidWaypoint(runway, NULL); - } else if (pieces.size() == 4) { - // navid/radial/navid/radial notation - FGPositionedRef p2 = FGPositioned::findClosestWithIdent(pieces[2], basePosition); - if (!p2) { - SG_LOG( SG_NAVAID, SG_INFO, "Unable to find FGPositioned with ident:" << pieces[2]); - return NULL; - } - - double r1 = atof(pieces[1].c_str()), - r2 = atof(pieces[3].c_str()); - r1 += magvar; - r2 += magvar; - - SGGeod intersection; - bool ok = SGGeodesy::radialIntersection(p->geod(), r1, p2->geod(), r2, intersection); - if (!ok) { - SG_LOG(SG_NAVAID, SG_INFO, "no valid intersection for:" << target); - return NULL; - } - - std::string name = p->ident() + "-" + p2->ident(); - wpt = new BasicWaypt(intersection, name, NULL); + const double magvar = magvarDegAt(basePosition); + + if (wpt) { + // already handled in the lat/lon test above + } else if (target.find("VIA ") == 0) { + wpt = viaFromString(basePosition, target); + } else { + string_list pieces(simgear::strutils::split(target, "/")); + FGPositionedRef p = FGPositioned::findClosestWithIdent(pieces.front(), basePosition); + if (!p) { + SG_LOG( SG_NAVAID, SG_INFO, "Unable to find FGPositioned with ident:" << pieces.front()); + return NULL; + } + + if (pieces.size() == 1) { + wpt = new NavaidWaypoint(p, NULL); + } else if (pieces.size() == 3) { + // navaid/radial/distance-nm notation + double radial = atof(pieces[1].c_str()), + distanceNm = atof(pieces[2].c_str()); + radial += magvar; + wpt = new OffsetNavaidWaypoint(p, NULL, radial, distanceNm); + } else if (pieces.size() == 2) { + FGAirport* apt = dynamic_cast(p.ptr()); + if (!apt) { + SG_LOG(SG_NAVAID, SG_INFO, "Waypoint is not an airport:" << pieces.front()); + return NULL; + } + + if (!apt->hasRunwayWithIdent(pieces[1])) { + SG_LOG(SG_NAVAID, SG_INFO, "No runway: " << pieces[1] << " at " << pieces[0]); + return NULL; + } + + FGRunway* runway = apt->getRunwayByIdent(pieces[1]); + wpt = new NavaidWaypoint(runway, NULL); + } else if (pieces.size() == 4) { + wpt = intersectionFromString(p, basePosition, magvar, pieces); + } } if (!wpt) { @@ -1084,7 +1120,51 @@ WayptRef FlightPlan::waypointFromString(const string& tgt ) } return wpt; } - + + +void FlightPlan::activate() +{ + lockDelegate(); + + _currentIndex = 0; + _currentWaypointChanged = true; + + for (unsigned int i=0; i < _legs.size(); ) { + if (_legs[i]->waypoint()->type() == "via") { + WayptRef preceeding = _legs[i - 1]->waypoint(); + Via* via = static_cast(_legs[i]->waypoint()); + WayptVec wps = via->expandToWaypoints(preceeding); + + // delete the VIA leg + LegVec::iterator it = _legs.begin(); + it += i; + Leg* l = *it; + _legs.erase(it); + delete l; + + // create new lefs and insert + it = _legs.begin(); + it += i; + + LegVec newLegs; + BOOST_FOREACH(WayptRef wp, wps) { + newLegs.push_back(new Leg(this, wp)); + } + + _waypointsChanged = true; + _legs.insert(it, newLegs.begin(), newLegs.end()); + } else { + ++i; // normal case, no expansion + } + } + + if (_delegate) { + _delegate->runActivated(); + } + + unlockDelegate(); +} + FlightPlan::Leg::Leg(FlightPlan* owner, WayptRef wpt) : _parent(owner), _speedRestrict(RESTRICT_NONE), diff --git a/src/Navaids/FlightPlan.hxx b/src/Navaids/FlightPlan.hxx index 34630e829..00e76d4fb 100644 --- a/src/Navaids/FlightPlan.hxx +++ b/src/Navaids/FlightPlan.hxx @@ -2,7 +2,7 @@ * FlightPlan.hxx - defines a full flight-plan object, including * departure, cruise, arrival information and waypoints */ - + // Written by James Turner, started 2012. // // Copyright (C) 2012 James Turner @@ -32,7 +32,7 @@ namespace flightgear class Transition; class FlightPlan; - + typedef SGSharedPtr FlightPlanRef; const char ICAO_AIRCRAFT_CATEGORY_A = 'A'; @@ -46,7 +46,7 @@ class FlightPlan : public RouteBase public: FlightPlan(); virtual ~FlightPlan(); - + virtual std::string ident() const; void setIdent(const std::string& s); @@ -60,7 +60,7 @@ public: void setIcaoAircraftCategory(const std::string& cat); FlightPlan* clone(const std::string& newIdent = std::string()) const; - + /** * flight-plan leg encapsulation */ @@ -69,37 +69,37 @@ public: public: FlightPlan* owner() const { return _parent; } - + Waypt* waypoint() const { return _waypt; } - + // reutrn the next leg after this one Leg* nextLeg() const; - + unsigned int index() const; - - int altitudeFt() const; + + int altitudeFt() const; int speed() const; - + int speedKts() const; double speedMach() const; - - RouteRestriction altitudeRestriction() const; + + RouteRestriction altitudeRestriction() const; RouteRestriction speedRestriction() const; - + void setSpeed(RouteRestriction ty, double speed); void setAltitude(RouteRestriction ty, int altFt); - + double courseDeg() const; double distanceNm() const; double distanceAlongRoute() const; private: friend class FlightPlan; - + Leg(FlightPlan* owner, WayptRef wpt); - + Leg* cloneFor(FlightPlan* owner) const; - + FlightPlan* _parent; RouteRestriction _speedRestrict, _altRestrict; int _speed; @@ -109,14 +109,14 @@ public: mutable double _pathDistance; mutable double _courseDeg; /// total distance of this leg from departure point - mutable double _distanceAlongPath; + mutable double _distanceAlongPath; }; - + class Delegate { public: virtual ~Delegate(); - + virtual void departureChanged() { } virtual void arrivalChanged() { } virtual void waypointsChanged() { } @@ -126,10 +126,10 @@ public: virtual void endOfFlightPlan() { } protected: Delegate(); - + private: void removeInner(Delegate* d); - + void runDepartureChanged(); void runArrivalChanged(); void runWaypointsChanged(); @@ -139,98 +139,98 @@ public: void runActivated(); friend class FlightPlan; - + bool _deleteWithPlan; Delegate* _inner; }; - + Leg* insertWayptAtIndex(Waypt* aWpt, int aIndex); void insertWayptsAtIndex(const WayptVec& wps, int aIndex); - + void deleteIndex(int index); void clear(); int clearWayptsWithFlag(WayptFlag flag); - + int currentIndex() const { return _currentIndex; } - + void setCurrentIndex(int index); void activate(); void finish(); - + Leg* currentLeg() const; Leg* nextLeg() const; Leg* previousLeg() const; - + int numLegs() const { return _legs.size(); } - + Leg* legAtIndex(int index) const; int findLegIndex(const Leg* l) const; - + int findWayptIndex(const SGGeod& aPos) const; int findWayptIndex(const FGPositionedRef aPos) const; - + bool load(const SGPath& p); bool save(const SGPath& p); - + FGAirportRef departureAirport() const { return _departure; } - + FGAirportRef destinationAirport() const { return _destination; } - + FGRunway* departureRunway() const { return _departureRunway; } - + FGRunway* destinationRunway() const { return _destinationRunway; } - + Approach* approach() const { return _approach; } - + void setDeparture(FGAirport* apt); void setDeparture(FGRunway* rwy); - + SID* sid() const { return _sid; } - + Transition* sidTransition() const; - + void setSID(SID* sid, const std::string& transition = std::string()); - + void setSID(Transition* sidWithTrans); - + void setDestination(FGAirport* apt); void setDestination(FGRunway* rwy); - + /** * note setting an approach will implicitly update the destination * airport and runway to match */ void setApproach(Approach* app); - + STAR* star() const { return _star; } - + Transition* starTransition() const; - + void setSTAR(STAR* star, const std::string& transition = std::string()); - + void setSTAR(Transition* starWithTrans); - + double totalDistanceNm() const { return _totalDistance; } - + /** * given a waypoint index, and an offset in NM, find the geodetic * position on the route path. I.e the point 10nm before or after * a particular waypoint. */ SGGeod pointAlongRoute(int aIndex, double aOffsetNm) const; - + /** * Create a WayPoint from a string in the following format: * - simple identifier @@ -239,7 +239,7 @@ public: * - navaid/radial-deg/offset-nm */ WayptRef waypointFromString(const std::string& target); - + /** * abstract interface for creating delegates automatically when a * flight-plan is created or loaded @@ -249,33 +249,33 @@ public: public: virtual Delegate* createFlightPlanDelegate(FlightPlan* fp) = 0; }; - + static void registerDelegateFactory(DelegateFactory* df); static void unregisterDelegateFactory(DelegateFactory* df); - + void addDelegate(Delegate* d); void removeDelegate(Delegate* d); private: void lockDelegate(); void unlockDelegate(); - + int _delegateLock; - bool _arrivalChanged, - _departureChanged, - _waypointsChanged, + bool _arrivalChanged, + _departureChanged, + _waypointsChanged, _currentWaypointChanged; - + bool loadXmlFormat(const SGPath& path); bool loadGpxFormat(const SGPath& path); bool loadPlainTextFormat(const SGPath& path); - + void loadVersion1XMLRoute(SGPropertyNode_ptr routeData); void loadVersion2XMLRoute(SGPropertyNode_ptr routeData); void loadXMLRouteHeader(SGPropertyNode_ptr routeData); WayptRef parseVersion1XMLWaypt(SGPropertyNode* aWP); - + double magvarDegAt(const SGGeod& pos) const; - + std::string _ident; int _currentIndex; bool _followLegTrackToFix; @@ -287,16 +287,16 @@ private: SGSharedPtr _star; SGSharedPtr _approach; std::string _sidTransition, _starTransition; - + double _totalDistance; void rebuildLegData(); - + typedef std::vector LegVec; LegVec _legs; - + Delegate* _delegate; }; - + } // of namespace flightgear #endif // of FG_FLIGHTPLAN_HXX diff --git a/src/Navaids/NavDataCache.cxx b/src/Navaids/NavDataCache.cxx index 3f60bbba9..a2b841397 100644 --- a/src/Navaids/NavDataCache.cxx +++ b/src/Navaids/NavDataCache.cxx @@ -84,9 +84,9 @@ using std::string; namespace { const int MAX_RETRIES = 10; - + const int CACHE_SIZE_KBYTES= 32 * 1024; - + // bind a std::string to a sqlite statement. The std::string must live the // entire duration of the statement execution - do not pass a temporary // std::string, or the compiler may delete it, freeing the C-string storage, @@ -102,7 +102,7 @@ void sqlite_bind_temp_stdstring(sqlite3_stmt* stmt, int value, const std::string { sqlite3_bind_text(stmt, value, s.c_str(), s.length(), SQLITE_TRANSIENT); } - + typedef sqlite3_stmt* sqlite3_stmt_ptr; void f_distanceCartSqrFunction(sqlite3_context* ctx, int argc, sqlite3_value* argv[]) @@ -110,30 +110,30 @@ void f_distanceCartSqrFunction(sqlite3_context* ctx, int argc, sqlite3_value* ar if (argc != 6) { return; } - + SGVec3d posA(sqlite3_value_double(argv[0]), sqlite3_value_double(argv[1]), sqlite3_value_double(argv[2])); - + SGVec3d posB(sqlite3_value_double(argv[3]), sqlite3_value_double(argv[4]), sqlite3_value_double(argv[5])); sqlite3_result_double(ctx, distSqr(posA, posB)); } - - + + static string cleanRunwayNo(const string& aRwyNo) { if (aRwyNo[0] == 'x') { return string(); // no ident for taxiways } - + string result(aRwyNo); // canonicalise runway ident if ((aRwyNo.size() == 1) || !isdigit(aRwyNo[1])) { result = "0" + aRwyNo; } - + // trim off trailing garbage if (result.size() > 2) { char suffix = toupper(result[2]); @@ -141,10 +141,10 @@ static string cleanRunwayNo(const string& aRwyNo) result = result.substr(0, 2); } } - + return result; } - + } // anonymous namespace namespace flightgear @@ -166,22 +166,22 @@ public: _completionPercent(0), _isFinished(false) { - + } - + bool isFinished() const { SGGuard g(_lock); return _isFinished; } - + virtual void run() { SGTimeStamp st; st.stamp(); _cache->doRebuild(); SG_LOG(SG_NAVCACHE, SG_INFO, "cache rebuild took:" << st.elapsedMSec() << "msec"); - + SGGuard g(_lock); _isFinished = true; _phase = NavDataCache::REBUILD_DONE; @@ -219,9 +219,9 @@ private: }; //////////////////////////////////////////////////////////////////////////// - + typedef std::map PositionedCache; - + class AirportTower : public FGPositioned { public: @@ -246,16 +246,16 @@ public: transactionAborted(false) { } - + ~NavDataCachePrivate() { close(); } - + void init() { SG_LOG(SG_NAVCACHE, SG_INFO, "NavCache at:" << path); - + readOnly = fgGetBool("/sim/fghome-readonly", false); int openFlags = readOnly ? SQLITE_OPEN_READONLY : @@ -264,13 +264,13 @@ public: // for the UTF8 / path logic here std::string pathUtf8 = simgear::strutils::convertWindowsLocal8BitToUtf8(path.str()); sqlite3_open_v2(pathUtf8.c_str(), &db, openFlags, NULL); - + sqlite3_stmt_ptr checkTables = prepare("SELECT count(*) FROM sqlite_master WHERE name='properties'"); - + sqlite3_create_function(db, "distanceCartSqr", 6, SQLITE_ANY, NULL, f_distanceCartSqrFunction, NULL, NULL); - + execSelect(checkTables); bool didCreate = false; if (!readOnly && (sqlite3_column_int(checkTables, 0) == 0)) { @@ -278,11 +278,11 @@ public: initTables(); didCreate = true; } - + readPropertyQuery = prepare("SELECT value FROM properties WHERE key=?"); writePropertyQuery = prepare("INSERT INTO properties (key, value) VALUES (?,?)"); clearProperty = prepare("DELETE FROM properties WHERE key=?1"); - + if (didCreate) { writeIntProperty("schema-version", SCHEMA_VERSION); } else { @@ -292,7 +292,7 @@ public: throw sg_exception("Navcache schema has changed"); } } - + // see http://www.sqlite.org/pragma.html#pragma_cache_size // for the details, small cache would cause thrashing. std::ostringstream q; @@ -300,7 +300,7 @@ public: runSQL(q.str()); prepareQueries(); } - + void close() { BOOST_FOREACH(sqlite3_stmt_ptr stmt, prepared) { @@ -309,34 +309,34 @@ public: prepared.clear(); sqlite3_close(db); } - + void checkCacheFile() { SG_LOG(SG_NAVCACHE, SG_INFO, "running DB integrity check"); SGTimeStamp st; st.stamp(); - + sqlite3_stmt_ptr stmt = prepare("PRAGMA quick_check(1)"); if (!execSelect(stmt)) { throw sg_exception("DB integrity check failed to run"); } - + string v = (char*) sqlite3_column_text(stmt, 0); if (v != "ok") { throw sg_exception("DB integrity check returned:" + v); } - + SG_LOG(SG_NAVCACHE, SG_INFO, "NavDataCache integrity check took:" << st.elapsedMSec()); finalize(stmt); } bool isCachedFileModified(const SGPath& path, bool verbose); - + void callSqlite(int result, const string& sql) { if (result == SQLITE_OK) return; // all good - + string errMsg; if (result == SQLITE_MISUSE) { errMsg = "Sqlite API abuse"; @@ -345,25 +345,25 @@ public: errMsg = sqlite3_errmsg(db); SG_LOG(SG_NAVCACHE, SG_ALERT, "Sqlite error:" << errMsg << " running:\n\t" << sql); } - + throw sg_exception("Sqlite error:" + errMsg, sql); } - + void runSQL(const string& sql) { sqlite3_stmt_ptr stmt; callSqlite(sqlite3_prepare_v2(db, sql.c_str(), sql.length(), &stmt, NULL), sql); - + try { execSelect(stmt); } catch (sg_exception&) { sqlite3_finalize(stmt); throw; // re-throw } - + sqlite3_finalize(stmt); } - + sqlite3_stmt_ptr prepare(const string& sql) { sqlite3_stmt_ptr stmt; @@ -371,18 +371,18 @@ public: prepared.push_back(stmt); return stmt; } - + void finalize(sqlite3_stmt_ptr s) { StmtVec::iterator it = std::find(prepared.begin(), prepared.end(), s); if (it == prepared.end()) { throw sg_exception("Finalising statement that was not prepared"); } - + prepared.erase(it); sqlite3_finalize(s); } - + void reset(sqlite3_stmt_ptr stmt) { assert(stmt); @@ -392,12 +392,12 @@ public: throw sg_exception("Sqlite error resetting:" + errMsg, sqlite3_sql(stmt)); } } - + bool execSelect(sqlite3_stmt_ptr stmt) { return stepSelect(stmt); } - + bool stepSelect(sqlite3_stmt_ptr stmt) { int retries = 0; @@ -407,24 +407,24 @@ public: if (result == SQLITE_ROW) { return true; // at least one result row } - + if (result == SQLITE_DONE) { return false; // no result rows } - + if (result != SQLITE_BUSY) { break; } - + SG_LOG(SG_NAVCACHE, SG_ALERT, "NavCache contention on select, will retry:" << retries); SGTimeStamp::sleepForMSec(++retries * 10); } // of retry loop for DB locked - + if (retries >= MAX_RETRIES) { SG_LOG(SG_NAVCACHE, SG_ALERT, "exceeded maximum number of SQLITE_BUSY retries"); return false; } - + string errMsg; if (result == SQLITE_MISUSE) { errMsg = "Sqlite API abuse"; @@ -434,10 +434,10 @@ public: SG_LOG(SG_NAVCACHE, SG_ALERT, "Sqlite error:" << errMsg << " (" << result << ") while running:\n\t" << sqlite3_sql(stmt)); } - + throw sg_exception("Sqlite error:" + errMsg, sqlite3_sql(stmt)); } - + void execSelect1(sqlite3_stmt_ptr stmt) { if (!execSelect(stmt)) { @@ -445,7 +445,7 @@ public: throw sg_exception("no results returned for select", sqlite3_sql(stmt)); } } - + sqlite3_int64 execInsert(sqlite3_stmt_ptr stmt) { execSelect(stmt); @@ -453,13 +453,13 @@ public: reset(stmt); return rowid; } - + void execUpdate(sqlite3_stmt_ptr stmt) { execSelect(stmt); reset(stmt); } - + void initTables() { string_list commands = simgear::strutils::split(SCHEMA_SQL, ";"); @@ -467,116 +467,116 @@ public: if (sql.empty()) { continue; } - + runSQL(sql); } // of commands in scheme loop } - + void prepareQueries() { writePropertyMulti = prepare("INSERT INTO properties (key, value) VALUES(?1,?2)"); - + beginTransactionStmt = prepare("BEGIN"); commitTransactionStmt = prepare("COMMIT"); rollbackTransactionStmt = prepare("ROLLBACK"); - + #define POSITIONED_COLS "rowid, type, ident, name, airport, lon, lat, elev_m, octree_node" #define AND_TYPED "AND type>=?2 AND type <=?3" statCacheCheck = prepare("SELECT stamp FROM stat_cache WHERE path=?"); stampFileCache = prepare("INSERT OR REPLACE INTO stat_cache " "(path, stamp) VALUES (?,?)"); - + loadPositioned = prepare("SELECT " POSITIONED_COLS " FROM positioned WHERE rowid=?"); loadAirportStmt = prepare("SELECT has_metar FROM airport WHERE rowid=?"); loadNavaid = prepare("SELECT range_nm, freq, multiuse, runway, colocated FROM navaid WHERE rowid=?"); loadCommStation = prepare("SELECT freq_khz, range_nm FROM comm WHERE rowid=?"); loadRunwayStmt = prepare("SELECT heading, length_ft, width_m, surface, displaced_threshold," "stopway, reciprocal, ils FROM runway WHERE rowid=?1"); - + getAirportItems = prepare("SELECT rowid FROM positioned WHERE airport=?1 " AND_TYPED); - + setAirportMetar = prepare("UPDATE airport SET has_metar=?2 WHERE rowid=" "(SELECT rowid FROM positioned WHERE ident=?1 AND type>=?3 AND type <=?4)"); sqlite3_bind_int(setAirportMetar, 3, FGPositioned::AIRPORT); sqlite3_bind_int(setAirportMetar, 4, FGPositioned::SEAPORT); - + setRunwayReciprocal = prepare("UPDATE runway SET reciprocal=?2 WHERE rowid=?1"); setRunwayILS = prepare("UPDATE runway SET ils=?2 WHERE rowid=?1"); setNavaidColocated = prepare("UPDATE navaid SET colocated=?2 WHERE rowid=?1"); - + insertPositionedQuery = prepare("INSERT INTO positioned " "(type, ident, name, airport, lon, lat, elev_m, octree_node, " "cart_x, cart_y, cart_z)" " VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11)"); - + setAirportPos = prepare("UPDATE positioned SET lon=?2, lat=?3, elev_m=?4, octree_node=?5, " "cart_x=?6, cart_y=?7, cart_z=?8 WHERE rowid=?1"); insertAirport = prepare("INSERT INTO airport (rowid, has_metar) VALUES (?, ?)"); insertNavaid = prepare("INSERT INTO navaid (rowid, freq, range_nm, multiuse, runway, colocated)" " VALUES (?1, ?2, ?3, ?4, ?5, ?6)"); - + insertCommStation = prepare("INSERT INTO comm (rowid, freq_khz, range_nm)" " VALUES (?, ?, ?)"); insertRunway = prepare("INSERT INTO runway " "(rowid, heading, length_ft, width_m, surface, displaced_threshold, stopway, reciprocal)" " VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)"); runwayLengthFtQuery = prepare("SELECT length_ft FROM runway WHERE rowid=?1"); - + removePOIQuery = prepare("DELETE FROM positioned WHERE type=?1 AND ident=?2"); - - // query statement + + // query statement findClosestWithIdent = prepare("SELECT rowid FROM positioned WHERE ident=?1 " AND_TYPED " ORDER BY distanceCartSqr(cart_x, cart_y, cart_z, ?4, ?5, ?6)"); - + findCommByFreq = prepare("SELECT positioned.rowid FROM positioned, comm WHERE " "positioned.rowid=comm.rowid AND freq_khz=?1 " AND_TYPED " ORDER BY distanceCartSqr(cart_x, cart_y, cart_z, ?4, ?5, ?6)"); - + findNavsByFreq = prepare("SELECT positioned.rowid FROM positioned, navaid WHERE " "positioned.rowid=navaid.rowid " "AND navaid.freq=?1 " AND_TYPED " ORDER BY distanceCartSqr(cart_x, cart_y, cart_z, ?4, ?5, ?6)"); - + findNavsByFreqNoPos = prepare("SELECT positioned.rowid FROM positioned, navaid WHERE " "positioned.rowid=navaid.rowid AND freq=?1 " AND_TYPED); - + findNavaidForRunway = prepare("SELECT positioned.rowid FROM positioned, navaid WHERE " "positioned.rowid=navaid.rowid AND runway=?1 AND type=?2"); - + // for an octree branch, return the child octree nodes which exist, // described as a bit-mask getOctreeChildren = prepare("SELECT children FROM octree WHERE rowid=?1"); - + #ifdef LAZY_OCTREE_UPDATES updateOctreeChildren = prepare("UPDATE octree SET children=?2 WHERE rowid=?1"); #else // mask the new child value into the existing one updateOctreeChildren = prepare("UPDATE octree SET children=(?2 | children) WHERE rowid=?1"); #endif - + // define a new octree node (with no children) insertOctree = prepare("INSERT INTO octree (rowid, children) VALUES (?1, 0)"); - + getOctreeLeafChildren = prepare("SELECT rowid, type FROM positioned WHERE octree_node=?1"); - + searchAirports = prepare("SELECT ident, name FROM positioned WHERE (name LIKE ?1 OR ident LIKE ?1) " AND_TYPED); sqlite3_bind_int(searchAirports, 2, FGPositioned::AIRPORT); sqlite3_bind_int(searchAirports, 3, FGPositioned::SEAPORT); - + getAllAirports = prepare("SELECT ident, name FROM positioned WHERE type>=?1 AND type <=?2"); sqlite3_bind_int(getAllAirports, 1, FGPositioned::AIRPORT); sqlite3_bind_int(getAllAirports, 2, FGPositioned::SEAPORT); - + getAirportItemByIdent = prepare("SELECT rowid FROM positioned WHERE airport=?1 AND ident=?2 AND type=?3"); - + findAirportRunway = prepare("SELECT airport, rowid FROM positioned WHERE ident=?2 AND type=?3 AND airport=" "(SELECT rowid FROM positioned WHERE type=?4 AND ident=?1)"); sqlite3_bind_int(findAirportRunway, 3, FGPositioned::RUNWAY); sqlite3_bind_int(findAirportRunway, 4, FGPositioned::AIRPORT); - + // three-way join to get the navaid ident and runway ident in a single select. // we're joining positioned to itself by the navaid runway, with the complication // that we need to join the navaids table to get the runway ID. @@ -588,33 +588,35 @@ public: sqlite3_bind_int(findILS, 4, FGPositioned::ILS); sqlite3_bind_int(findILS, 5, FGPositioned::LOC); - - // airways + + // airways findAirway = prepare("SELECT rowid FROM airway WHERE network=?1 AND ident=?2"); insertAirway = prepare("INSERT INTO airway (ident, network) " "VALUES (?1, ?2)"); - + insertAirwayEdge = prepare("INSERT INTO airway_edge (network, airway, a, b) " "VALUES (?1, ?2, ?3, ?4)"); - + isPosInAirway = prepare("SELECT rowid FROM airway_edge WHERE network=?1 AND a=?2"); - + airwayEdgesFrom = prepare("SELECT airway, b FROM airway_edge WHERE network=?1 AND a=?2"); + + airwayEdges = prepare("SELECT a, b FROM airway_edge WHERE airway=?1"); } - + void writeIntProperty(const string& key, int value) { sqlite_bind_stdstring(clearProperty, 1, key); execUpdate(clearProperty); - + sqlite_bind_stdstring(writePropertyQuery, 1, key); sqlite3_bind_int(writePropertyQuery, 2, value); execUpdate(writePropertyQuery); } - + FGPositioned* loadById(sqlite_int64 rowId, sqlite3_int64& aptId); - + FGAirport* loadAirport(sqlite_int64 rowId, FGPositioned::Type ty, const string& id, const string& name, const SGGeod& pos) @@ -623,21 +625,21 @@ public: execSelect1(loadAirportStmt); bool hasMetar = (sqlite3_column_int(loadAirportStmt, 0) > 0); reset(loadAirportStmt); - + return new FGAirport(rowId, id, pos, name, hasMetar, ty); } - + FGRunwayBase* loadRunway(sqlite3_int64 rowId, FGPositioned::Type ty, const string& id, const SGGeod& pos, PositionedID apt) { sqlite3_bind_int(loadRunwayStmt, 1, rowId); execSelect1(loadRunwayStmt); - + double heading = sqlite3_column_double(loadRunwayStmt, 0); double lengthM = sqlite3_column_int(loadRunwayStmt, 1); double widthM = sqlite3_column_double(loadRunwayStmt, 2); int surface = sqlite3_column_int(loadRunwayStmt, 3); - + if (ty == FGPositioned::TAXIWAY) { reset(loadRunwayStmt); return new FGTaxiway(rowId, id, pos, heading, lengthM, widthM, surface); @@ -651,20 +653,20 @@ public: PositionedID ils = sqlite3_column_int64(loadRunwayStmt, 7); FGRunway* r = new FGRunway(rowId, apt, id, pos, heading, lengthM, widthM, displacedThreshold, stopway, surface); - + if (reciprocal > 0) { r->setReciprocalRunway(reciprocal); } - + if (ils > 0) { r->setILS(ils); } - + reset(loadRunwayStmt); return r; } } - + CommStation* loadComm(sqlite3_int64 rowId, FGPositioned::Type ty, const string& id, const string& name, const SGGeod& pos, @@ -672,23 +674,23 @@ public: { sqlite3_bind_int64(loadCommStation, 1, rowId); execSelect1(loadCommStation); - + int range = sqlite3_column_int(loadCommStation, 0); int freqKhz = sqlite3_column_int(loadCommStation, 1); reset(loadCommStation); - + CommStation* c = new CommStation(rowId, name, ty, pos, freqKhz, range); c->setAirport(airport); return c; } - + FGPositioned* loadNav(sqlite3_int64 rowId, FGPositioned::Type ty, const string& id, const string& name, const SGGeod& pos) { sqlite3_bind_int64(loadNavaid, 1, rowId); execSelect1(loadNavaid); - + PositionedID runway = sqlite3_column_int64(loadNavaid, 3); // marker beacons are light-weight if ((ty == FGPositioned::OM) || (ty == FGPositioned::IM) || @@ -697,7 +699,7 @@ public: reset(loadNavaid); return new FGMarkerBeaconRecord(rowId, ty, runway, pos); } - + int rangeNm = sqlite3_column_int(loadNavaid, 0), freq = sqlite3_column_int(loadNavaid, 1); double mulituse = sqlite3_column_double(loadNavaid, 2); @@ -716,13 +718,13 @@ public: return n; } - + PositionedID insertPositioned(FGPositioned::Type ty, const string& ident, const string& name, const SGGeod& pos, PositionedID apt, bool spatialIndex) { SGVec3d cartPos(SGVec3d::fromGeod(pos)); - + sqlite3_bind_int(insertPositionedQuery, 1, ty); sqlite_bind_stdstring(insertPositionedQuery, 2, ident); sqlite_bind_stdstring(insertPositionedQuery, 3, name); @@ -730,7 +732,7 @@ public: sqlite3_bind_double(insertPositionedQuery, 5, pos.getLongitudeDeg()); sqlite3_bind_double(insertPositionedQuery, 6, pos.getLatitudeDeg()); sqlite3_bind_double(insertPositionedQuery, 7, pos.getElevationM()); - + if (spatialIndex) { Octree::Leaf* octreeLeaf = Octree::global_spatialOctree->findLeafForPos(cartPos); assert(intersects(octreeLeaf->bbox(), cartPos)); @@ -738,21 +740,21 @@ public: } else { sqlite3_bind_null(insertPositionedQuery, 8); } - + sqlite3_bind_double(insertPositionedQuery, 9, cartPos.x()); sqlite3_bind_double(insertPositionedQuery, 10, cartPos.y()); sqlite3_bind_double(insertPositionedQuery, 11, cartPos.z()); - - PositionedID r = execInsert(insertPositionedQuery); + + PositionedID r = execInsert(insertPositionedQuery); return r; } - + FGPositionedList findAllByString(const string& s, const string& column, FGPositioned::Filter* filter, bool exact) { string query = s; if (!exact) query += "%"; - + // build up SQL query text string matchTerm = exact ? "=?1" : " LIKE ?1"; string sql = "SELECT rowid FROM positioned WHERE " + column + matchTerm; @@ -772,7 +774,7 @@ public: sqlite3_bind_int(stmt, 2, filter->minType()); sqlite3_bind_int(stmt, 3, filter->maxType()); } - + FGPositionedList result; // run the prepared SQL while (stepSelect(stmt)) @@ -781,14 +783,14 @@ public: if (filter && !filter->pass(pos)) { continue; } - + result.push_back(pos); } - + reset(stmt); return result; } - + PositionedIDVec selectIds(sqlite3_stmt_ptr query) { PositionedIDVec result; @@ -798,7 +800,7 @@ public: reset(query); return result; } - + double runwayLengthFt(PositionedID rwy) { sqlite3_bind_int64(runwayLengthFtQuery, 1, rwy); @@ -807,7 +809,7 @@ public: reset(runwayLengthFtQuery); return length; } - + void flushDeferredOctreeUpdates() { BOOST_FOREACH(Octree::Branch* nd, deferredOctreeUpdates) { @@ -815,10 +817,10 @@ public: sqlite3_bind_int(updateOctreeChildren, 2, nd->childMask()); execUpdate(updateOctreeChildren); } - + deferredOctreeUpdates.clear(); } - + void removePositionedWithIdent(FGPositioned::Type ty, const std::string& aIdent) { sqlite3_bind_int(removePOIQuery, 1, ty); @@ -826,12 +828,12 @@ public: execUpdate(removePOIQuery); reset(removePOIQuery); } - + NavDataCache* outer; sqlite3* db; SGPath path; bool readOnly; - + /// the actual cache of ID -> instances. This holds an owning reference, /// so once items are in the cache they will never be deleted until /// the cache drops its reference @@ -844,22 +846,22 @@ public: unsigned int transactionLevel; bool transactionAborted; sqlite3_stmt_ptr beginTransactionStmt, commitTransactionStmt, rollbackTransactionStmt; - + SGPath aptDatPath, metarDatPath, navDatPath, fixDatPath, poiDatPath, carrierDatPath, airwayDatPath; - + sqlite3_stmt_ptr readPropertyQuery, writePropertyQuery, stampFileCache, statCacheCheck, loadAirportStmt, loadCommStation, loadPositioned, loadNavaid, loadRunwayStmt; sqlite3_stmt_ptr writePropertyMulti, clearProperty; - + sqlite3_stmt_ptr insertPositionedQuery, insertAirport, insertTower, insertRunway, insertCommStation, insertNavaid; sqlite3_stmt_ptr setAirportMetar, setRunwayReciprocal, setRunwayILS, setNavaidColocated, setAirportPos; sqlite3_stmt_ptr removePOIQuery; - + sqlite3_stmt_ptr findClosestWithIdent; // octree (spatial index) related queries sqlite3_stmt_ptr getOctreeChildren, insertOctree, updateOctreeChildren, @@ -871,40 +873,41 @@ public: sqlite3_stmt_ptr getAirportItems, getAirportItemByIdent; sqlite3_stmt_ptr findAirportRunway, findILS; - + sqlite3_stmt_ptr runwayLengthFtQuery; - + // airways - sqlite3_stmt_ptr findAirway, insertAirwayEdge, isPosInAirway, airwayEdgesFrom, - insertAirway; - + sqlite3_stmt_ptr findAirway, insertAirwayEdge, + isPosInAirway, airwayEdgesFrom, + insertAirway, airwayEdges; + // since there's many permutations of ident/name queries, we create // them programtically, but cache the exact query by its raw SQL once // used. std::map findByStringDict; - + typedef std::vector StmtVec; StmtVec prepared; - + std::set deferredOctreeUpdates; - + // if we're performing a rebuild, the thread that is doing the work. // otherwise, NULL std::auto_ptr rebuilder; }; ////////////////////////////////////////////////////////////////////// - + FGPositioned* NavDataCache::NavDataCachePrivate::loadById(sqlite3_int64 rowid, sqlite3_int64& aptId) { - + sqlite3_bind_int64(loadPositioned, 1, rowid); execSelect1(loadPositioned); - + assert(rowid == sqlite3_column_int64(loadPositioned, 0)); FGPositioned::Type ty = (FGPositioned::Type) sqlite3_column_int(loadPositioned, 1); - + PositionedID prowid = static_cast(rowid); string ident = (char*) sqlite3_column_text(loadPositioned, 2); string name = (char*) sqlite3_column_text(loadPositioned, 3); @@ -913,23 +916,23 @@ FGPositioned* NavDataCache::NavDataCachePrivate::loadById(sqlite3_int64 rowid, double lat = sqlite3_column_double(loadPositioned, 6); double elev = sqlite3_column_double(loadPositioned, 7); SGGeod pos = SGGeod::fromDegM(lon, lat, elev); - + reset(loadPositioned); - + switch (ty) { case FGPositioned::AIRPORT: case FGPositioned::SEAPORT: case FGPositioned::HELIPORT: return loadAirport(rowid, ty, ident, name, pos); - + case FGPositioned::TOWER: return new AirportTower(prowid, aptId, ident, pos); - + case FGPositioned::RUNWAY: case FGPositioned::HELIPAD: case FGPositioned::TAXIWAY: return loadRunway(rowid, ty, ident, pos, aptId); - + case FGPositioned::LOC: case FGPositioned::VOR: case FGPositioned::GS: @@ -942,10 +945,10 @@ FGPositioned* NavDataCache::NavDataCachePrivate::loadById(sqlite3_int64 rowid, case FGPositioned::TACAN: case FGPositioned::MOBILE_TACAN: return loadNav(rowid, ty, ident, name, pos); - + case FGPositioned::FIX: return new FGFix(rowid, ident, pos); - + case FGPositioned::WAYPOINT: case FGPositioned::COUNTRY: case FGPositioned::CITY: @@ -955,7 +958,7 @@ FGPositioned* NavDataCache::NavDataCachePrivate::loadById(sqlite3_int64 rowid, FGPositioned* wpt = new FGPositioned(rowid, ty, ident, pos); return wpt; } - + case FGPositioned::FREQ_GROUND: case FGPositioned::FREQ_TOWER: case FGPositioned::FREQ_ATIS: @@ -965,18 +968,18 @@ FGPositioned* NavDataCache::NavDataCachePrivate::loadById(sqlite3_int64 rowid, case FGPositioned::FREQ_CLEARANCE: case FGPositioned::FREQ_UNICOM: return loadComm(rowid, ty, ident, name, pos, aptId); - + default: return NULL; } } - + bool NavDataCache::NavDataCachePrivate::isCachedFileModified(const SGPath& path, bool verbose) { if (!path.exists()) { throw sg_io_exception("isCachedFileModified: Missing file:" + path.str()); } - + sqlite_bind_temp_stdstring(statCacheCheck, 1, path.str()); bool isModified = true; sgDebugPriority logLevel = verbose ? SG_WARN : SG_DEBUG; @@ -992,23 +995,23 @@ bool NavDataCache::NavDataCachePrivate::isCachedFileModified(const SGPath& path, { SG_LOG(SG_NAVCACHE, SG_DEBUG, "NavCache: no rebuild required for " << path); } - + isModified = (delta != 0); } else { SG_LOG(SG_NAVCACHE, logLevel, "NavCache: initial build required for " << path); } - + reset(statCacheCheck); return isModified; } - + static NavDataCache* static_instance = NULL; - + NavDataCache::NavDataCache() { const int MAX_TRIES = 3; SGPath homePath(globals->get_fg_home()); - + std::ostringstream os; string_list versionParts = simgear::strutils::split(VERSION, "."); if (versionParts.size() < 2) { @@ -1016,7 +1019,7 @@ NavDataCache::NavDataCache() } else { os << "navdata_" << versionParts[0] << "_" << versionParts[1] << ".cache"; } - + homePath.append(os.str()); // permit additional DB connections from the same process @@ -1033,26 +1036,26 @@ NavDataCache::NavDataCache() SG_LOG(SG_NAVCACHE, t == 0 ? SG_WARN : SG_ALERT, "NavCache: init failed:" << e.what() << " (attempt " << t << ")"); d.reset(); - + // only wipe the existing if not readonly if (!fgGetBool("/sim/fghome-readonly", false)) { homePath.remove(); } } } // of retry loop - + double RADIUS_EARTH_M = 7000 * 1000.0; // 7000km is plenty SGVec3d earthExtent(RADIUS_EARTH_M, RADIUS_EARTH_M, RADIUS_EARTH_M); Octree::global_spatialOctree = new Octree::Branch(SGBox(-earthExtent, earthExtent), 1); - + d->aptDatPath = SGPath(globals->get_fg_root()); d->aptDatPath.append("Airports/apt.dat.gz"); - + d->metarDatPath = SGPath(globals->get_fg_root()); d->metarDatPath.append("Airports/metar.dat.gz"); - d->navDatPath = SGPath(globals->get_fg_root()); + d->navDatPath = SGPath(globals->get_fg_root()); d->navDatPath.append("Navaids/nav.dat.gz"); d->fixDatPath = SGPath(globals->get_fg_root()); @@ -1060,10 +1063,10 @@ NavDataCache::NavDataCache() d->poiDatPath = SGPath(globals->get_fg_root()); d->poiDatPath.append("Navaids/poi.dat.gz"); - + d->carrierDatPath = SGPath(globals->get_fg_root()); d->carrierDatPath.append("Navaids/carrier_nav.dat.gz"); - + d->airwayDatPath = SGPath(globals->get_fg_root()); d->airwayDatPath.append("Navaids/awy.dat.gz"); } @@ -1080,23 +1083,23 @@ NavDataCache* NavDataCache::createInstance() static_instance = new NavDataCache; return static_instance; } - + NavDataCache* NavDataCache::instance() { return static_instance; } - + bool NavDataCache::isRebuildRequired() { if (d->readOnly) { return false; } - + if (flightgear::Options::sharedInstance()->isOptionSet("restore-defaults")) { SG_LOG(SG_NAVCACHE, SG_INFO, "NavCache: restore-defaults requested, will rebuild cache"); return true; } - + if (d->isCachedFileModified(d->aptDatPath, true) || d->isCachedFileModified(d->metarDatPath, true) || d->isCachedFileModified(d->navDatPath, true) || @@ -1156,14 +1159,14 @@ void NavDataCache::doRebuild() d->close(); // completely close the sqlite object d->path.remove(); // remove the file on disk d->init(); // start again from scratch - + // initialise the root octree node d->runSQL("INSERT INTO octree (rowid, children) VALUES (1, 0)"); - + SGTimeStamp st; { Transaction txn(this); - + st.stamp(); airportDBLoad(d->aptDatPath); SG_LOG(SG_NAVCACHE, SG_INFO, "apt.dat load took:" << st.elapsedMSec()); @@ -1172,12 +1175,12 @@ void NavDataCache::doRebuild() metarDataLoad(d->metarDatPath); stampCacheFile(d->aptDatPath); stampCacheFile(d->metarDatPath); - + st.stamp(); loadFixes(d->fixDatPath); stampCacheFile(d->fixDatPath); SG_LOG(SG_NAVCACHE, SG_INFO, "fix.dat load took:" << st.elapsedMSec()); - + st.stamp(); navDBInit(d->navDatPath); stampCacheFile(d->navDatPath); @@ -1188,13 +1191,13 @@ void NavDataCache::doRebuild() txn.commit(); SG_LOG(SG_NAVCACHE, SG_INFO, "stage 1 commit took:" << st.elapsedMSec()); } - + #ifdef SG_WINDOWS SG_LOG(SG_NAVCACHE, SG_ALERT, "SKIPPING POI load on Windows"); #else { Transaction txn(this); - + st.stamp(); poiDBInit(d->poiDatPath); stampCacheFile(d->poiDatPath); @@ -1206,22 +1209,22 @@ void NavDataCache::doRebuild() SG_LOG(SG_NAVCACHE, SG_INFO, "POI commit took:" << st.elapsedMSec()); } #endif - + { Transaction txn(this); loadCarrierNav(d->carrierDatPath); stampCacheFile(d->carrierDatPath); - + st.stamp(); Airway::load(d->airwayDatPath); stampCacheFile(d->airwayDatPath); SG_LOG(SG_NAVCACHE, SG_INFO, "awy.dat load took:" << st.elapsedMSec()); - + d->flushDeferredOctreeUpdates(); - + string sceneryPaths = simgear::strutils::join(globals->get_fg_scenery(), ";"); writeStringProperty("scenery_paths", sceneryPaths); - + st.stamp(); txn.commit(); SG_LOG(SG_NAVCACHE, SG_INFO, "final commit took:" << st.elapsedMSec()); @@ -1232,18 +1235,18 @@ void NavDataCache::doRebuild() SG_LOG(SG_NAVCACHE, SG_ALERT, "caught exception rebuilding navCache:" << e.what()); } } - + int NavDataCache::readIntProperty(const string& key) { sqlite_bind_stdstring(d->readPropertyQuery, 1, key); int result = 0; - + if (d->execSelect(d->readPropertyQuery)) { result = sqlite3_column_int(d->readPropertyQuery, 0); } else { SG_LOG(SG_NAVCACHE, SG_WARN, "readIntProperty: unknown:" << key); } - + d->reset(d->readPropertyQuery); return result; } @@ -1257,11 +1260,11 @@ double NavDataCache::readDoubleProperty(const string& key) } else { SG_LOG(SG_NAVCACHE, SG_WARN, "readDoubleProperty: unknown:" << key); } - + d->reset(d->readPropertyQuery); return result; } - + string NavDataCache::readStringProperty(const string& key) { sqlite_bind_stdstring(d->readPropertyQuery, 1, key); @@ -1271,7 +1274,7 @@ string NavDataCache::readStringProperty(const string& key) } else { SG_LOG(SG_NAVCACHE, SG_WARN, "readStringProperty: unknown:" << key); } - + d->reset(d->readPropertyQuery); return result; } @@ -1295,7 +1298,7 @@ void NavDataCache::writeDoubleProperty(const string& key, const double& value) { sqlite_bind_stdstring(d->clearProperty, 1, key); d->execUpdate(d->clearProperty); - + sqlite_bind_stdstring(d->writePropertyQuery, 1, key); sqlite3_bind_double(d->writePropertyQuery, 2, value); d->execUpdate(d->writePropertyQuery); @@ -1309,22 +1312,22 @@ string_list NavDataCache::readStringListProperty(const string& key) result.push_back((char*) sqlite3_column_text(d->readPropertyQuery, 0)); } d->reset(d->readPropertyQuery); - + return result; } - + void NavDataCache::writeStringListProperty(const string& key, const string_list& values) { sqlite_bind_stdstring(d->clearProperty, 1, key); d->execUpdate(d->clearProperty); - + BOOST_FOREACH(string value, values) { sqlite_bind_stdstring(d->writePropertyMulti, 1, key); sqlite_bind_stdstring(d->writePropertyMulti, 2, value); d->execInsert(d->writePropertyMulti); } } - + bool NavDataCache::isCachedFileModified(const SGPath& path) const { return d->isCachedFileModified(path, false); @@ -1344,10 +1347,10 @@ void NavDataCache::beginTransaction() d->stepSelect(d->beginTransactionStmt); sqlite3_reset(d->beginTransactionStmt); } - + ++d->transactionLevel; } - + void NavDataCache::commitTransaction() { assert(d->transactionLevel > 0); @@ -1356,7 +1359,7 @@ void NavDataCache::commitTransaction() // still abort the entire transaction. That's bad, but safer than // committing. sqlite3_stmt_ptr q = d->transactionAborted ? d->rollbackTransactionStmt : d->commitTransactionStmt; - + int retries = 0; int result; while (retries < MAX_RETRIES) { @@ -1364,7 +1367,7 @@ void NavDataCache::commitTransaction() if (result == SQLITE_DONE) { break; } - + // see http://www.sqlite.org/c3ref/get_autocommit.html for a hint // what's going on here: autocommit in inactive inside BEGIN, so if // it's active, the DB was rolled-back @@ -1373,36 +1376,36 @@ void NavDataCache::commitTransaction() d->transactionAborted = true; break; } - + if (result != SQLITE_BUSY) { break; } - + SGTimeStamp::sleepForMSec(++retries * 10); SG_LOG(SG_NAVCACHE, SG_ALERT, "NavCache contention on commit, will retry:" << retries); } // of retry loop for DB busy - + string errMsg; if (result != SQLITE_DONE) { errMsg = sqlite3_errmsg(d->db); SG_LOG(SG_NAVCACHE, SG_ALERT, "Sqlite error:" << errMsg << " for " << result << " while running:\n\t" << sqlite3_sql(q)); } - + sqlite3_reset(q); } } - + void NavDataCache::abortTransaction() { SG_LOG(SG_NAVCACHE, SG_WARN, "NavCache: aborting transaction"); - + assert(d->transactionLevel > 0); if (--d->transactionLevel == 0) { d->stepSelect(d->rollbackTransactionStmt); sqlite3_reset(d->rollbackTransactionStmt); } - + d->transactionAborted = true; } @@ -1411,13 +1414,13 @@ FGPositionedRef NavDataCache::loadById(PositionedID rowid) if (rowid == 0) { return NULL; } - + PositionedCache::iterator it = d->cache.find(rowid); if (it != d->cache.end()) { d->cacheHits++; return it->second; // cache it } - + sqlite3_int64 aptId; FGPositioned* pos = d->loadById(rowid, aptId); d->cache.insert(it, PositionedCache::value_type(rowid, pos)); @@ -1428,7 +1431,7 @@ FGPositionedRef NavDataCache::loadById(PositionedID rowid) FGAirport* apt = FGPositioned::loadById(aptId); apt->validateILSData(); } - + return pos; } @@ -1441,27 +1444,27 @@ PositionedID NavDataCache::insertAirport(FGPositioned::Type ty, const string& id sqlite3_int64 rowId = d->insertPositioned(ty, ident, name, SGGeod(), 0 /* airport */, false /* spatial index */); - + sqlite3_bind_int64(d->insertAirport, 1, rowId); d->execInsert(d->insertAirport); - + return rowId; } - + void NavDataCache::updatePosition(PositionedID item, const SGGeod &pos) { if (d->cache.find(item) != d->cache.end()) { SG_LOG(SG_NAVCACHE, SG_DEBUG, "updating position of an item in the cache"); d->cache[item]->modifyPosition(pos); } - + SGVec3d cartPos(SGVec3d::fromGeod(pos)); - + sqlite3_bind_int(d->setAirportPos, 1, item); sqlite3_bind_double(d->setAirportPos, 2, pos.getLongitudeDeg()); sqlite3_bind_double(d->setAirportPos, 3, pos.getLatitudeDeg()); sqlite3_bind_double(d->setAirportPos, 4, pos.getElevationM()); - + // bug 905; the octree leaf may change here, but the leaf may already be // loaded, and caching its children. (Either the old or new leaf!). Worse, // we may be called here as a result of loading one of those leaf's children. @@ -1472,12 +1475,12 @@ void NavDataCache::updatePosition(PositionedID item, const SGGeod &pos) // which was updated above. Octree::Leaf* octreeLeaf = Octree::global_spatialOctree->findLeafForPos(cartPos); sqlite3_bind_int64(d->setAirportPos, 5, octreeLeaf->guid()); - + sqlite3_bind_double(d->setAirportPos, 6, cartPos.x()); sqlite3_bind_double(d->setAirportPos, 7, cartPos.y()); sqlite3_bind_double(d->setAirportPos, 8, cartPos.z()); - + d->execUpdate(d->setAirportPos); } @@ -1496,7 +1499,7 @@ NavDataCache::insertRunway(FGPositioned::Type ty, const string& ident, // only runways are spatially indexed; don't bother indexing taxiways // or pavements bool spatialIndex = ( ty == FGPositioned::RUNWAY || ty == FGPositioned::HELIPAD); - + sqlite3_int64 rowId = d->insertPositioned(ty, cleanRunwayNo(ident), "", pos, apt, spatialIndex); sqlite3_bind_int64(d->insertRunway, 1, rowId); @@ -1506,8 +1509,8 @@ NavDataCache::insertRunway(FGPositioned::Type ty, const string& ident, sqlite3_bind_int(d->insertRunway, 5, surfaceCode); sqlite3_bind_double(d->insertRunway, 6, displacedThreshold); sqlite3_bind_double(d->insertRunway, 7, stopway); - - return d->execInsert(d->insertRunway); + + return d->execInsert(d->insertRunway); } void NavDataCache::setRunwayReciprocal(PositionedID runway, PositionedID recip) @@ -1515,7 +1518,7 @@ void NavDataCache::setRunwayReciprocal(PositionedID runway, PositionedID recip) sqlite3_bind_int64(d->setRunwayReciprocal, 1, runway); sqlite3_bind_int64(d->setRunwayReciprocal, 2, recip); d->execUpdate(d->setRunwayReciprocal); - + // and the opposite direction too! sqlite3_bind_int64(d->setRunwayReciprocal, 2, runway); sqlite3_bind_int64(d->setRunwayReciprocal, 1, recip); @@ -1527,14 +1530,14 @@ void NavDataCache::setRunwayILS(PositionedID runway, PositionedID ils) sqlite3_bind_int64(d->setRunwayILS, 1, runway); sqlite3_bind_int64(d->setRunwayILS, 2, ils); d->execUpdate(d->setRunwayILS); - + // and the in-memory one if (d->cache.find(runway) != d->cache.end()) { FGRunway* instance = (FGRunway*) d->cache[runway].ptr(); instance->setILS(ils); } } - + PositionedID NavDataCache::insertNavaid(FGPositioned::Type ty, const string& ident, const string& name, const SGGeod& pos, @@ -1545,7 +1548,7 @@ NavDataCache::insertNavaid(FGPositioned::Type ty, const string& ident, if (ty == FGPositioned::MOBILE_TACAN) { spatialIndex = false; } - + sqlite3_int64 rowId = d->insertPositioned(ty, ident, name, pos, apt, spatialIndex); sqlite3_bind_int64(d->insertNavaid, 1, rowId); @@ -1570,7 +1573,7 @@ void NavDataCache::setNavaidColocated(PositionedID navaid, PositionedID colocate rec->setColocatedDME(colocatedDME); } } - + PositionedID NavDataCache::insertCommStation(FGPositioned::Type ty, const string& name, const SGGeod& pos, int freq, int range, PositionedID apt) @@ -1581,7 +1584,7 @@ PositionedID NavDataCache::insertCommStation(FGPositioned::Type ty, sqlite3_bind_int(d->insertCommStation, 3, range); return d->execInsert(d->insertCommStation); } - + PositionedID NavDataCache::insertFix(const std::string& ident, const SGGeod& aPos) { return d->insertPositioned(FGPositioned::FIX, ident, string(), aPos, 0, true); @@ -1592,15 +1595,15 @@ PositionedID NavDataCache::createPOI(FGPositioned::Type ty, const std::string& i return d->insertPositioned(ty, ident, string(), aPos, 0, true /* spatial index */); } - + bool NavDataCache::removePOI(FGPositioned::Type ty, const std::string& aIdent) { d->removePositionedWithIdent(ty, aIdent); // should remove from the live cache too? - + return true; } - + void NavDataCache::setAirportMetar(const string& icao, bool hasMetar) { sqlite_bind_stdstring(d->setAirportMetar, 1, icao); @@ -1637,29 +1640,29 @@ FGPositionedRef NavDataCache::findClosestWithIdent( const string& aIdent, sqlite3_bind_int(d->findClosestWithIdent, 2, FGPositioned::INVALID); sqlite3_bind_int(d->findClosestWithIdent, 3, FGPositioned::LAST_TYPE); } - + SGVec3d cartPos(SGVec3d::fromGeod(aPos)); sqlite3_bind_double(d->findClosestWithIdent, 4, cartPos.x()); sqlite3_bind_double(d->findClosestWithIdent, 5, cartPos.y()); sqlite3_bind_double(d->findClosestWithIdent, 6, cartPos.z()); - + FGPositionedRef result; - + while (d->stepSelect(d->findClosestWithIdent)) { FGPositioned* pos = loadById(sqlite3_column_int64(d->findClosestWithIdent, 0)); if (aFilter && !aFilter->pass(pos)) { continue; } - + result = pos; break; } - + d->reset(d->findClosestWithIdent); return result; } - + int NavDataCache::getOctreeBranchChildren(int64_t octreeNodeId) { sqlite3_bind_int64(d->getOctreeChildren, 1, octreeNodeId); @@ -1673,13 +1676,13 @@ void NavDataCache::defineOctreeNode(Octree::Branch* pr, Octree::Node* nd) { sqlite3_bind_int64(d->insertOctree, 1, nd->guid()); d->execInsert(d->insertOctree); - + #ifdef LAZY_OCTREE_UPDATES d->deferredOctreeUpdates.insert(pr); #else // lowest three bits of node ID are 0..7 index of the child in the parent int childIndex = nd->guid() & 0x07; - + sqlite3_bind_int64(d->updateOctreeChildren, 1, pr->guid()); // mask has bit N set where child N exists int childMask = 1 << childIndex; @@ -1687,12 +1690,12 @@ void NavDataCache::defineOctreeNode(Octree::Branch* pr, Octree::Node* nd) d->execUpdate(d->updateOctreeChildren); #endif } - + TypedPositionedVec NavDataCache::getOctreeLeafChildren(int64_t octreeNodeId) { sqlite3_bind_int64(d->getOctreeLeafChildren, 1, octreeNodeId); - + TypedPositionedVec r; while (d->stepSelect(d->getOctreeLeafChildren)) { FGPositioned::Type ty = static_cast @@ -1705,7 +1708,7 @@ NavDataCache::getOctreeLeafChildren(int64_t octreeNodeId) return r; } - + /** * A special purpose helper (used by FGAirport::searchNamesAndIdents) to * implement the AirportList dialog. It's unfortunate that it needs to reside @@ -1723,7 +1726,7 @@ char** NavDataCache::searchAirportNamesAndIdents(const std::string& aFilter) stmt = d->searchAirports; sqlite_bind_stdstring(stmt, 1, searchTerm); } - + char** result = (char**) malloc(sizeof(char*) * numAllocated); while (d->stepSelect(stmt)) { if ((numMatches + 1) >= numAllocated) { @@ -1734,7 +1737,7 @@ char** NavDataCache::searchAirportNamesAndIdents(const std::string& aFilter) free(result); result = nresult; } - + // nasty code to avoid excessive string copying and allocations. // We format results as follows (note whitespace!): // ' name-of-airport-chars (ident)' @@ -1761,12 +1764,12 @@ char** NavDataCache::searchAirportNamesAndIdents(const std::string& aFilter) result[numMatches++] = entry; } - + result[numMatches] = NULL; // end of list marker d->reset(stmt); return result; } - + FGPositionedRef NavDataCache::findCommByFreq(int freqKhz, const SGGeod& aPos, FGPositioned::Filter* aFilter) { @@ -1778,27 +1781,27 @@ NavDataCache::findCommByFreq(int freqKhz, const SGGeod& aPos, FGPositioned::Filt sqlite3_bind_int(d->findCommByFreq, 2, FGPositioned::FREQ_GROUND); sqlite3_bind_int(d->findCommByFreq, 3, FGPositioned::FREQ_UNICOM); } - + SGVec3d cartPos(SGVec3d::fromGeod(aPos)); sqlite3_bind_double(d->findCommByFreq, 4, cartPos.x()); sqlite3_bind_double(d->findCommByFreq, 5, cartPos.y()); sqlite3_bind_double(d->findCommByFreq, 6, cartPos.z()); FGPositionedRef result; - + while (d->execSelect(d->findCommByFreq)) { FGPositioned* p = loadById(sqlite3_column_int64(d->findCommByFreq, 0)); if (aFilter && !aFilter->pass(p)) { continue; } - + result = p; break; } - + d->reset(d->findCommByFreq); return result; } - + PositionedIDVec NavDataCache::findNavaidsByFreq(int freqKhz, const SGGeod& aPos, FGPositioned::Filter* aFilter) { @@ -1810,12 +1813,12 @@ NavDataCache::findNavaidsByFreq(int freqKhz, const SGGeod& aPos, FGPositioned::F sqlite3_bind_int(d->findNavsByFreq, 2, FGPositioned::NDB); sqlite3_bind_int(d->findNavsByFreq, 3, FGPositioned::GS); } - + SGVec3d cartPos(SGVec3d::fromGeod(aPos)); sqlite3_bind_double(d->findNavsByFreq, 4, cartPos.x()); sqlite3_bind_double(d->findNavsByFreq, 5, cartPos.y()); sqlite3_bind_double(d->findNavsByFreq, 6, cartPos.z()); - + return d->selectIds(d->findNavsByFreq); } @@ -1830,10 +1833,10 @@ NavDataCache::findNavaidsByFreq(int freqKhz, FGPositioned::Filter* aFilter) sqlite3_bind_int(d->findNavsByFreqNoPos, 2, FGPositioned::NDB); sqlite3_bind_int(d->findNavsByFreqNoPos, 3, FGPositioned::GS); } - + return d->selectIds(d->findNavsByFreqNoPos); } - + PositionedIDVec NavDataCache::airportItemsOfType(PositionedID apt,FGPositioned::Type ty, FGPositioned::Type maxTy) @@ -1841,11 +1844,11 @@ NavDataCache::airportItemsOfType(PositionedID apt,FGPositioned::Type ty, if (maxTy == FGPositioned::INVALID) { maxTy = ty; // single-type range } - + sqlite3_bind_int64(d->getAirportItems, 1, apt); sqlite3_bind_int(d->getAirportItems, 2, ty); sqlite3_bind_int(d->getAirportItems, 3, maxTy); - + return d->selectIds(d->getAirportItems); } @@ -1857,22 +1860,22 @@ NavDataCache::airportItemWithIdent(PositionedID apt, FGPositioned::Type ty, sqlite_bind_stdstring(d->getAirportItemByIdent, 2, ident); sqlite3_bind_int(d->getAirportItemByIdent, 3, ty); PositionedID result = 0; - + if (d->execSelect(d->getAirportItemByIdent)) { result = sqlite3_column_int64(d->getAirportItemByIdent, 0); } - + d->reset(d->getAirportItemByIdent); return result; } - + AirportRunwayPair NavDataCache::findAirportRunway(const std::string& aName) { if (aName.empty()) { return AirportRunwayPair(); } - + string_list parts = simgear::strutils::split(aName); if (parts.size() < 2) { SG_LOG(SG_NAVCACHE, SG_WARN, "findAirportRunway: malformed name:" << aName); @@ -1882,7 +1885,7 @@ NavDataCache::findAirportRunway(const std::string& aName) AirportRunwayPair result; sqlite_bind_stdstring(d->findAirportRunway, 1, parts[0]); sqlite_bind_stdstring(d->findAirportRunway, 2, cleanRunwayNo(parts[1])); - + if (d->execSelect(d->findAirportRunway)) { result = AirportRunwayPair(sqlite3_column_int64(d->findAirportRunway, 0), sqlite3_column_int64(d->findAirportRunway, 1)); @@ -1894,12 +1897,12 @@ NavDataCache::findAirportRunway(const std::string& aName) d->reset(d->findAirportRunway); return result; } - + PositionedID NavDataCache::findILS(PositionedID airport, const string& aRunway, const string& navIdent) { string runway(cleanRunwayNo(aRunway)); - + sqlite_bind_stdstring(d->findILS, 1, navIdent); sqlite3_bind_int64(d->findILS, 2, airport); sqlite_bind_stdstring(d->findILS, 3, runway); @@ -1907,16 +1910,16 @@ NavDataCache::findILS(PositionedID airport, const string& aRunway, const string& if (d->execSelect(d->findILS)) { result = sqlite3_column_int64(d->findILS, 0); } - + d->reset(d->findILS); return result; } - + int NavDataCache::findAirway(int network, const string& aName) { sqlite3_bind_int(d->findAirway, 1, network); sqlite_bind_stdstring(d->findAirway, 2, aName); - + int airway = 0; if (d->execSelect(d->findAirway)) { // already exists @@ -1926,7 +1929,7 @@ int NavDataCache::findAirway(int network, const string& aName) sqlite3_bind_int(d->insertAirway, 2, network); airway = d->execInsert(d->insertAirway); } - + d->reset(d->findAirway); return airway; } @@ -1940,18 +1943,18 @@ void NavDataCache::insertEdge(int network, int airwayID, PositionedID from, Posi sqlite3_bind_int64(d->insertAirwayEdge, 3, from); sqlite3_bind_int64(d->insertAirwayEdge, 4, to); d->execInsert(d->insertAirwayEdge); - + std::swap(from, to); } } - + bool NavDataCache::isInAirwayNetwork(int network, PositionedID pos) { sqlite3_bind_int(d->isPosInAirway, 1, network); sqlite3_bind_int64(d->isPosInAirway, 2, pos); bool ok = d->execSelect(d->isPosInAirway); d->reset(d->isPosInAirway); - + return ok; } @@ -1959,7 +1962,7 @@ AirwayEdgeVec NavDataCache::airwayEdgesFrom(int network, PositionedID pos) { sqlite3_bind_int(d->airwayEdgesFrom, 1, network); sqlite3_bind_int64(d->airwayEdgesFrom, 2, pos); - + AirwayEdgeVec result; while (d->stepSelect(d->airwayEdgesFrom)) { result.push_back(AirwayEdge( @@ -1967,21 +1970,90 @@ AirwayEdgeVec NavDataCache::airwayEdgesFrom(int network, PositionedID pos) sqlite3_column_int64(d->airwayEdgesFrom, 1) )); } - + d->reset(d->airwayEdgesFrom); return result; } +PositionedIDVec NavDataCache::airwayWaypts(int id) +{ + sqlite3_bind_int(d->airwayEdges, 1, id); + + typedef std::pair Edge; + typedef std::deque EdgeVec; + typedef std::deque PositionedIDDeque; + +// build up the EdgeVec, order is arbitrary + EdgeVec rawEdges; + while (d->stepSelect(d->airwayEdges)) { + rawEdges.push_back(Edge(sqlite3_column_int64(d->airwayEdges, 0), + sqlite3_column_int64(d->airwayEdges, 1) + )); + } + + d->reset(d->airwayEdges); + if (rawEdges.empty()) { + return PositionedIDVec(); + } + +// linearize + PositionedIDDeque linearAirway; + PositionedID firstId = rawEdges.front().first, + lastId = rawEdges.front().second; + std::set seen; + + // first edge is trivial + linearAirway.push_back(firstId); + linearAirway.push_back(lastId); + seen.insert(firstId); + seen.insert(lastId); + rawEdges.pop_front(); + + while (!rawEdges.empty()) { + Edge e = rawEdges.front(); + rawEdges.pop_front(); + + // look for new segments + if (e.first == firstId) { + linearAirway.push_front(e.second); + seen.insert(e.second); + firstId = e.second; + continue; + } + + if (e.first == lastId) { + linearAirway.push_back(e.second); + seen.insert(e.second); + lastId = e.second; + continue; + } + + // look for seen segments - presumed to be reversed internal edges + if (seen.find(e.first) != seen.end()) { + // if it's the inverse of interior edge, both ends must have been + // seen. Otherwise it should have been an exterior edge and + // handled by the case above. + assert(seen.find(e.second) != seen.end()); + continue; + } + + // push back to try later on + rawEdges.push_back(e); + } + + return PositionedIDVec(linearAirway.begin(), linearAirway.end()); +} + PositionedID NavDataCache::findNavaidForRunway(PositionedID runway, FGPositioned::Type ty) { sqlite3_bind_int64(d->findNavaidForRunway, 1, runway); sqlite3_bind_int(d->findNavaidForRunway, 2, ty); - + PositionedID result = 0; if (d->execSelect(d->findNavaidForRunway)) { result = sqlite3_column_int64(d->findNavaidForRunway, 0); } - + d->reset(d->findNavaidForRunway); return result; } @@ -1998,7 +2070,7 @@ SGPath NavDataCache::path() const ///////////////////////////////////////////////////////////////////////////////////////// // Transaction RAII object - + NavDataCache::Transaction::Transaction(NavDataCache* cache) : _instance(cache), _committed(false) @@ -2106,6 +2178,5 @@ bool NavDataCache::ThreadedGUISearch::isComplete() const SGGuard g(d->lock); return d->isComplete; } - -} // of namespace flightgear +} // of namespace flightgear diff --git a/src/Navaids/NavDataCache.hxx b/src/Navaids/NavDataCache.hxx index 615be64f9..5d58829de 100644 --- a/src/Navaids/NavDataCache.hxx +++ b/src/Navaids/NavDataCache.hxx @@ -2,7 +2,7 @@ * NavDataCache.hxx - defines a unified binary cache for navigation * data, parsed from various text / XML sources. */ - + // Written by James Turner, started 2012. // // Copyright (C) 2012 James Turner @@ -28,33 +28,33 @@ #include // for string_list #include - + class SGPath; class FGRunway; namespace flightgear { - + /// a pair of airport ID, runway ID typedef std::pair AirportRunwayPair; - + typedef std::pair TypedPositioned; typedef std::vector TypedPositionedVec; // pair of airway ID, destination node ID typedef std::pair AirwayEdge; typedef std::vector AirwayEdgeVec; - + namespace Octree { class Node; class Branch; } - + class NavDataCache { public: ~NavDataCache(); - + // singleton accessor static NavDataCache* instance(); @@ -62,7 +62,7 @@ public: static NavDataCache* createInstance(); SGPath path() const; - + /** * predicate - check if the cache needs to be rebuilt. * This can happen is the cache file is missing or damaged, or one of the @@ -90,25 +90,25 @@ public: bool isCachedFileModified(const SGPath& path) const; void stampCacheFile(const SGPath& path); - + int readIntProperty(const std::string& key); double readDoubleProperty(const std::string& key); std::string readStringProperty(const std::string& key); - + void writeIntProperty(const std::string& key, int value); void writeStringProperty(const std::string& key, const std::string& value); void writeDoubleProperty(const std::string& key, const double& value); - + string_list readStringListProperty(const std::string& key); void writeStringListProperty(const std::string& key, const string_list& values); - + /** * retrieve an FGPositioned from the cache. * This may be trivial if the object is previously loaded, or require actual * disk IO. */ FGPositionedRef loadById(PositionedID guid); - + PositionedID insertAirport(FGPositioned::Type ty, const std::string& ident, const std::string& name); void insertTower(PositionedID airportId, const SGGeod& pos); @@ -118,42 +118,42 @@ public: double stopway, int surfaceCode); void setRunwayReciprocal(PositionedID runway, PositionedID recip); void setRunwayILS(PositionedID runway, PositionedID ils); - + PositionedID insertNavaid(FGPositioned::Type ty, const std::string& ident, const std::string& name, const SGGeod& pos, int freq, int range, double multiuse, PositionedID apt, PositionedID runway); // Assign colocated DME to a navaid void setNavaidColocated(PositionedID navaid, PositionedID colocatedDME); - + PositionedID insertCommStation(FGPositioned::Type ty, const std::string& name, const SGGeod& pos, int freq, int range, PositionedID apt); PositionedID insertFix(const std::string& ident, const SGGeod& aPos); - + PositionedID createPOI(FGPositioned::Type ty, const std::string& ident, const SGGeod& aPos); - + bool removePOI(FGPositioned::Type ty, const std::string& aIdent); - + /// update the metar flag associated with an airport void setAirportMetar(const std::string& icao, bool hasMetar); - + /** * Modify the position of an existing item. */ void updatePosition(PositionedID item, const SGGeod &pos); - + FGPositionedList findAllWithIdent( const std::string& ident, FGPositioned::Filter* filter, bool exact ); FGPositionedList findAllWithName( const std::string& ident, FGPositioned::Filter* filter, bool exact ); - + FGPositionedRef findClosestWithIdent( const std::string& aIdent, const SGGeod& aPos, FGPositioned::Filter* aFilter ); - + /** * Helper to implement the AirportSearch widget. Optimised text search of @@ -161,35 +161,35 @@ public: * to PLIB. */ char** searchAirportNamesAndIdents(const std::string& aFilter); - + /** * Find the closest matching comm-station on a frequency, to a position. * The filter with be used for both type ranging and to validate the result * candidates. */ FGPositionedRef findCommByFreq(int freqKhz, const SGGeod& pos, FGPositioned::Filter* filt); - + /** * find all items of a specified type (or range of types) at an airport */ PositionedIDVec airportItemsOfType(PositionedID apt, FGPositioned::Type ty, FGPositioned::Type maxTy = FGPositioned::INVALID); - + /** * find the first match item of the specified type and ident, at an airport */ PositionedID airportItemWithIdent(PositionedID apt, FGPositioned::Type ty, const std::string& ident); - + /** * Find all navaids matching a particular frequency, sorted by range from the * supplied position. Type-range will be determined from the filter */ PositionedIDVec findNavaidsByFreq(int freqKhz, const SGGeod& pos, FGPositioned::Filter* filt); - + /// overload version of the above that does not consider positioned when /// returning results. Only used by TACAN carrier search PositionedIDVec findNavaidsByFreq(int freqKhz, FGPositioned::Filter* filt); - + /** * Given a runway and type, find the corresponding navaid (ILS / GS / OM) */ @@ -201,30 +201,30 @@ public: * Such names look like: 'LHBP 31L DME-ILS' or 'UEEE 23L MM' */ AirportRunwayPair findAirportRunway(const std::string& name); - + /** * Given an aiport, and runway and ILS identifier, find the corresponding cache * entry. This matches the data we get in the ils.xml files for airports. */ PositionedID findILS(PositionedID airport, const std::string& runway, const std::string& navIdent); - + /** * Given an Octree node ID, return a bit-mask defining which of the child * nodes exist. In practice this means an 8-bit value be sufficent, but * an int works fine too. */ int getOctreeBranchChildren(int64_t octreeNodeId); - + void defineOctreeNode(Octree::Branch* pr, Octree::Node* nd); - + /** * given an octree leaf, return all its child positioned items and their types */ TypedPositionedVec getOctreeLeafChildren(int64_t octreeNodeId); - + // airways int findAirway(int network, const std::string& aName); - + /** * insert an edge between two positioned nodes, into the network. * The airway identifier will be set accordingly. No reverse edge is created @@ -232,28 +232,33 @@ public: * created. */ void insertEdge(int network, int airwayID, PositionedID from, PositionedID to); - + /// is the specified positioned a node on the network? bool isInAirwayNetwork(int network, PositionedID pos); - + /** * retrive all the destination points reachcable from a positioned * in an airway */ AirwayEdgeVec airwayEdgesFrom(int network, PositionedID pos); - + + /** + * Waypoints on the airway + */ + PositionedIDVec airwayWaypts(int id); + class Transaction { public: Transaction(NavDataCache* cache); ~Transaction(); - + void commit(); private: NavDataCache* _instance; bool _committed; }; - + bool isReadOnly() const; class ThreadedGUISearch @@ -261,7 +266,7 @@ public: public: ThreadedGUISearch(const std::string& term); ~ThreadedGUISearch(); - + PositionedIDVec results() const; bool isComplete() const; @@ -271,21 +276,20 @@ public: }; private: NavDataCache(); - + friend class RebuildThread; void doRebuild(); - + friend class Transaction; - + void beginTransaction(); void commitTransaction(); void abortTransaction(); - + class NavDataCachePrivate; - std::auto_ptr d; + std::auto_ptr d; }; - + } // of namespace flightgear #endif // of FG_NAVDATACACHE_HXX - diff --git a/src/Navaids/airways.cxx b/src/Navaids/airways.cxx index 0000cdfa0..c0459836d 100644 --- a/src/Navaids/airways.cxx +++ b/src/Navaids/airways.cxx @@ -161,11 +161,106 @@ void Airway::load(const SGPath& path) } // of file line iteration } +WayptVec::const_iterator Airway::find(WayptRef wpt) const +{ + WayptVec::const_iterator it; + for (it = _elements.begin(); it != _elements.end(); ++it) { + if (wpt->matches(*it)) { + return it; + } + } + + return it; +} + +bool Airway::canVia(const WayptRef& from, const WayptRef& to) const +{ + WayptVec::const_iterator fit = find(from); + WayptVec::const_iterator tit = find(to); + + if ((fit == _elements.end()) || (tit == _elements.end())) { + return false; + } + + return true; +} + +WayptVec Airway::via(const WayptRef& from, const WayptRef& to) const +{ + WayptVec v; + WayptVec::const_iterator fit = find(from); + WayptVec::const_iterator tit = find(to); + + if ((fit == _elements.end()) || (tit == _elements.end())) { + throw sg_exception("bad VIA transition points"); + } + + if (fit == tit) { + // will cause duplicate point but that seems better than + // return an empty + v.push_back(*tit); + return v; + } + + // establish the ordering of the transitions, i.e are we moving forward or + // backard along the airway. + if (fit < tit) { + // forward progression + for (++fit; fit != tit; ++fit) { + v.push_back(*fit); + } + } else { + // reverse progression + for (--fit; fit != tit; --fit) { + v.push_back(*fit); + } + } + + v.push_back(*tit); + return v; +} + +bool Airway::containsNavaid(const FGPositionedRef &navaid) const +{ + return find(new NavaidWaypoint(navaid, NULL)) != _elements.end(); +} + int Airway::Network::findAirway(const std::string& aName, double aTop, double aBase) { return NavDataCache::instance()->findAirway(_networkID, aName); } +Airway* Airway::findByIdent(const std::string& aIdent) +{ + NavDataCache* ndc = NavDataCache::instance(); + + int id = ndc->findAirway(0, aIdent); + + PositionedIDVec pts = ndc->airwayWaypts(id); + Airway* awy = new Airway(aIdent, 0, 0); + + PositionedIDVec::const_iterator it; + for (it = pts.begin(); it != pts.end(); ++it) { + FGPositionedRef pos = ndc->loadById(*it); + WayptRef w = new NavaidWaypoint(pos, NULL); + awy->_elements.push_back(w); + } + + return awy; +} + +WayptRef Airway::findEnroute(const std::string &aIdent) const +{ + WayptVec::const_iterator it; + for (it = _elements.begin(); it != _elements.end(); ++it) { + if ((*it)->ident() == aIdent) { + return *it; + } + } + + return WayptRef(); +} + void Airway::Network::addEdge(int aWay, const SGGeod& aStartPos, const std::string& aStartIdent, const SGGeod& aEndPos, const std::string& aEndIdent) diff --git a/src/Navaids/airways.hxx b/src/Navaids/airways.hxx index 8062ee41e..b7f1d1f02 100644 --- a/src/Navaids/airways.hxx +++ b/src/Navaids/airways.hxx @@ -49,7 +49,17 @@ public: double bottomAltitudeFt() const { return _bottomAltitudeFt; } - + + static Airway* findByIdent(const std::string& aIdent); + + WayptRef findEnroute(const std::string& aIdent) const; + + bool canVia(const WayptRef& from, const WayptRef& to) const; + + WayptVec via(const WayptRef& from, const WayptRef& to) const; + + bool containsNavaid(const FGPositionedRef& navaid) const; + /** * Track a network of airways * @@ -70,6 +80,8 @@ public: * Returns true if a route could be found, or false otherwise. */ bool route(WayptRef aFrom, WayptRef aTo, WayptVec& aPath); + + private: void addEdge(int aWay, const SGGeod& aStartPos, const std::string& aStartIdent, @@ -123,6 +135,8 @@ public: private: Airway(const std::string& aIdent, double aTop, double aBottom); + WayptVec::const_iterator find(WayptRef wpt) const; + friend class Network; std::string _ident; diff --git a/src/Navaids/route.cxx b/src/Navaids/route.cxx index 0697769d3..c4c0c5220 100644 --- a/src/Navaids/route.cxx +++ b/src/Navaids/route.cxx @@ -212,7 +212,11 @@ Waypt* Waypt::createInstance(RouteBase* aOwner, const std::string& aTypeName) r = new RadialIntercept(aOwner); } else if (aTypeName == "vectors") { r = new ATCVectors(aOwner); - } + } else if (aTypeName == "discontinuity") { + r = new Discontinuity(aOwner); + } else if (aTypeName == "via") { + r = new Via(aOwner); + } if (!r || (r->type() != aTypeName)) { throw sg_exception("broken factory method for type:" + aTypeName, diff --git a/src/Navaids/waypoint.cxx b/src/Navaids/waypoint.cxx index 5d2d76101..6d728491c 100644 --- a/src/Navaids/waypoint.cxx +++ b/src/Navaids/waypoint.cxx @@ -27,6 +27,7 @@ #include #include +#include using std::string; @@ -465,4 +466,121 @@ void ATCVectors::writeToProperties(SGPropertyNode_ptr aProp) const 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 diff --git a/src/Navaids/waypoint.hxx b/src/Navaids/waypoint.hxx index f0ed8b890..c70a17b12 100644 --- a/src/Navaids/waypoint.hxx +++ b/src/Navaids/waypoint.hxx @@ -312,7 +312,55 @@ private: * suffices until we have a proper facility representation */ FGAirportRef _facility; -}; +}; + +/** + * Represent a route discontinuity. These can occur while editing + * plans via certain interfaces (such as CDUs) + */ +class Discontinuity : public Waypt +{ +public: + virtual ~Discontinuity(); + Discontinuity(RouteBase* aOwner); + + virtual void initFromProperties(SGPropertyNode_ptr aProp); + virtual void writeToProperties(SGPropertyNode_ptr aProp) const; + + virtual std::string type() const + { return "discontinuity"; } + + virtual SGGeod position() const; + + virtual std::string ident() const; + + virtual double magvarDeg() const + { return 0.0; } +private: +}; + +class Via : public Waypt +{ +public: + Via(RouteBase* aOwner); + Via(RouteBase* aOwner, const std::string& airwayName, FGPositioned* to); + virtual ~Via(); + + virtual void initFromProperties(SGPropertyNode_ptr aProp); + virtual void writeToProperties(SGPropertyNode_ptr aProp) const; + + virtual std::string type() const + { return "via"; } + + virtual SGGeod position() const; + + virtual std::string ident() const; + + WayptVec expandToWaypoints(WayptRef aPreceeding) const; +private: + std::string _airway; + FGPositionedRef _to; +}; } // of namespace flighgear -- 2.39.5