#include "sqlite3.h"
// SimGear
+#include <simgear/sg_inlines.h>
#include <simgear/structure/exception.hxx>
#include <simgear/debug/logstream.hxx>
#include <simgear/bucket/newbucket.hxx>
#include <Main/globals.hxx>
#include "markerbeacon.hxx"
#include "navrecord.hxx"
-#include <Airports/simple.hxx>
+#include <Airports/airport.hxx>
#include <Airports/runways.hxx>
#include <ATC/CommStation.hxx>
#include "fix.hxx"
#include "PositionedOctree.hxx"
#include <Airports/apt_loader.hxx>
#include <Navaids/airways.hxx>
+#include "poidb.hxx"
#include <Airports/parking.hxx>
#include <Airports/gnnode.hxx>
namespace {
const int MAX_RETRIES = 10;
-const int SCHEMA_VERSION = 6;
-
+const int SCHEMA_VERSION = 8;
+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,
~NavDataCachePrivate()
{
- BOOST_FOREACH(sqlite3_stmt_ptr stmt, prepared) {
- sqlite3_finalize(stmt);
- }
- prepared.clear();
-
- sqlite3_close(db);
+ close();
}
void init()
{
SG_LOG(SG_NAVCACHE, SG_INFO, "NavCache at:" << path);
- sqlite3_open_v2(path.c_str(), &db,
+
+ // see http://code.google.com/p/flightgear-bugs/issues/detail?id=1055
+ // for the logic here. Sigh.
+ std::string pathUtf8 = simgear::strutils::convertWindowsLocal8BitToUtf8(path.str());
+ sqlite3_open_v2(pathUtf8.c_str(), &db,
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
}
}
+ // see http://www.sqlite.org/pragma.html#pragma_cache_size
+ // for the details, small cache would cause thrashing.
+ std::ostringstream q;
+ q << "PRAGMA cache_size=-" << CACHE_SIZE_KBYTES << ";";
+ runSQL(q.str());
prepareQueries();
}
+ void close()
+ {
+ BOOST_FOREACH(sqlite3_stmt_ptr stmt, prepared) {
+ sqlite3_finalize(stmt);
+ }
+ prepared.clear();
+ sqlite3_close(db);
+ }
+
void checkCacheFile()
{
SG_LOG(SG_NAVCACHE, SG_INFO, "running DB integrity check");
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");
updateRunwayThreshold = prepare("UPDATE runway SET heading=?2, displaced_threshold=?3, stopway=?4 WHERE rowid=?1");
insertPositionedQuery = prepare("INSERT INTO positioned "
" 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
findClosestWithIdent = prepare("SELECT rowid FROM positioned WHERE ident=?1 "
AND_TYPED " ORDER BY distanceCartSqr(cart_x, cart_y, cart_z, ?4, ?5, ?6)");
if (ty == FGPositioned::TAXIWAY) {
reset(loadRunwayStmt);
return new FGTaxiway(rowId, id, pos, heading, lengthM, widthM, surface);
+ } else if (ty == FGPositioned::HELIPAD) {
+ reset(loadRunwayStmt);
+ return new FGHelipad(rowId, apt, id, pos, heading, lengthM, widthM, surface);
} else {
double displacedThreshold = sqlite3_column_double(loadRunwayStmt, 4);
double stopway = sqlite3_column_double(loadRunwayStmt, 5);
PositionedID reciprocal = sqlite3_column_int64(loadRunwayStmt, 6);
PositionedID ils = sqlite3_column_int64(loadRunwayStmt, 7);
FGRunway* r = new FGRunway(rowId, apt, id, pos, heading, lengthM, widthM,
- displacedThreshold, stopway, surface, false);
+ displacedThreshold, stopway, surface);
if (reciprocal > 0) {
r->setReciprocalRunway(reciprocal);
}
int rangeNm = sqlite3_column_int(loadNavaid, 0),
- freq = sqlite3_column_int(loadNavaid, 1);
+ freq = sqlite3_column_int(loadNavaid, 1);
double mulituse = sqlite3_column_double(loadNavaid, 2);
- //sqlite3_int64 colocated = sqlite3_column_int64(loadNavaid, 4);
+ PositionedID colocated = sqlite3_column_int64(loadNavaid, 4);
reset(loadNavaid);
-
- return new FGNavRecord(rowId, ty, id, name, pos, freq, rangeNm, mulituse, runway);
+
+ FGNavRecord* n = new FGNavRecord(rowId, ty, id, name, pos, freq, rangeNm, mulituse, runway);
+ if (colocated) {
+ n->setColocatedDME(colocated);
+ }
+
+ return n;
}
FGPositioned* loadParking(sqlite3_int64 rowId,
return r;
}
- FGPositioned::List findAllByString(const string& s, const string& column,
+ FGPositionedList findAllByString(const string& s, const string& column,
FGPositioned::Filter* filter, bool exact)
{
string query = s;
sqlite3_bind_int(stmt, 3, filter->maxType());
}
- FGPositioned::List result;
+ FGPositionedList result;
// run the prepared SQL
while (stepSelect(stmt))
{
deferredOctreeUpdates.clear();
}
+
+ void removePositionedWithIdent(FGPositioned::Type ty, const std::string& aIdent)
+ {
+ sqlite3_bind_int(removePOIQuery, 1, ty);
+ sqlite_bind_stdstring(removePOIQuery, 2, aIdent);
+ execUpdate(removePOIQuery);
+ reset(removePOIQuery);
+ }
NavDataCache* outer;
sqlite3* db;
bool transactionAborted;
sqlite3_stmt_ptr beginTransactionStmt, commitTransactionStmt, rollbackTransactionStmt;
- SGPath aptDatPath, metarDatPath, navDatPath, fixDatPath,
+ SGPath aptDatPath, metarDatPath, navDatPath, fixDatPath, poiDatPath,
carrierDatPath, airwayDatPath;
sqlite3_stmt_ptr readPropertyQuery, writePropertyQuery,
sqlite3_stmt_ptr insertPositionedQuery, insertAirport, insertTower, insertRunway,
insertCommStation, insertNavaid;
- sqlite3_stmt_ptr setAirportMetar, setRunwayReciprocal, setRunwayILS,
+ sqlite3_stmt_ptr setAirportMetar, setRunwayReciprocal, setRunwayILS, setNavaidColocated,
setAirportPos, updateRunwayThreshold, updateILS;
+ sqlite3_stmt_ptr removePOIQuery;
sqlite3_stmt_ptr findClosestWithIdent;
// octree (spatial index) related queries
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) {
return new AirportTower(rowid, aptId, ident, pos);
case FGPositioned::RUNWAY:
+ case FGPositioned::HELIPAD:
case FGPositioned::TAXIWAY:
return loadRunway(rowid, ty, ident, pos, aptId);
case FGPositioned::MOBILE_TACAN:
{
if (aptId > 0) {
- FGAirport* apt = (FGAirport*) outer->loadById(aptId);
+ FGAirport* apt = FGPositioned::loadById<FGAirport>(aptId);
if (apt->validateILSData()) {
- SG_LOG(SG_NAVCACHE, SG_INFO, "re-loaded ILS data for " << apt->ident());
// queried data above is probably invalid, force us to go around again
// (the next time through, validateILSData will return false)
return outer->loadById(rowid);
return new FGFix(rowid, ident, pos);
case FGPositioned::WAYPOINT:
+ case FGPositioned::COUNTRY:
+ case FGPositioned::CITY:
+ case FGPositioned::TOWN:
+ case FGPositioned::VILLAGE:
{
- FGPositioned* wpt = new FGPositioned(rowid, FGPositioned::WAYPOINT, ident, pos);
+ FGPositioned* wpt = new FGPositioned(rowid, ty, ident, pos);
return wpt;
}
{
const int MAX_TRIES = 3;
SGPath homePath(globals->get_fg_home());
- homePath.append("navdata.cache");
+
+ std::ostringstream os;
+ string_list versionParts = simgear::strutils::split(VERSION, ".");
+ if (versionParts.size() < 2) {
+ os << "navdata.cache";
+ } else {
+ os << "navdata_" << versionParts[0] << "_" << versionParts[1] << ".cache";
+ }
+
+ homePath.append(os.str());
for (int t=0; t < MAX_TRIES; ++t) {
try {
d->fixDatPath = SGPath(globals->get_fg_root());
d->fixDatPath.append("Navaids/fix.dat.gz");
+
+ 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");
{
assert(static_instance == this);
static_instance = NULL;
- SG_LOG(SG_NAVCACHE, SG_INFO, "closing the navcache");
d.reset();
}
isCachedFileModified(d->metarDatPath) ||
isCachedFileModified(d->navDatPath) ||
isCachedFileModified(d->fixDatPath) ||
+ isCachedFileModified(d->poiDatPath) ||
isCachedFileModified(d->airwayDatPath))
{
SG_LOG(SG_NAVCACHE, SG_INFO, "NavCache: main cache rebuild required");
void NavDataCache::doRebuild()
{
try {
- Transaction txn(this);
- d->runSQL("DELETE FROM positioned");
- d->runSQL("DELETE FROM airport");
- d->runSQL("DELETE FROM runway");
- d->runSQL("DELETE FROM navaid");
- d->runSQL("DELETE FROM comm");
- d->runSQL("DELETE FROM octree");
- d->runSQL("DELETE FROM airway");
- d->runSQL("DELETE FROM airway_edge");
- d->runSQL("DELETE FROM taxi_node");
- d->runSQL("DELETE FROM parking");
- d->runSQL("DELETE FROM groundnet_edge");
- d->runSQL("DELETE FROM stat_cache");
-
- // initialise the root octree node
- d->runSQL("INSERT INTO octree (rowid, children) VALUES (1, 0)");
+ 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;
- st.stamp();
-
- airportDBLoad(d->aptDatPath);
- SG_LOG(SG_NAVCACHE, SG_INFO, "apt.dat load took:" << st.elapsedMSec());
-
- 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);
- SG_LOG(SG_NAVCACHE, SG_INFO, "nav.dat load took:" << st.elapsedMSec());
-
- 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);
-
- txn.commit();
+ {
+ Transaction txn(this);
+
+ st.stamp();
+ airportDBLoad(d->aptDatPath);
+ SG_LOG(SG_NAVCACHE, SG_INFO, "apt.dat load took:" << st.elapsedMSec());
+
+ 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);
+ SG_LOG(SG_NAVCACHE, SG_INFO, "nav.dat load took:" << st.elapsedMSec());
+
+ st.stamp();
+ 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);
+ SG_LOG(SG_NAVCACHE, SG_INFO, "poi.dat load took:" << st.elapsedMSec());
+
+ st.stamp();
+ txn.commit();
+ 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());
+
+ }
+
} catch (sg_exception& e) {
SG_LOG(SG_NAVCACHE, SG_ALERT, "caught exception rebuilding navCache:" << e.what());
}
if (delta != 0)
{
SG_LOG(SG_NAVCACHE, SG_DEBUG, "NavCache: rebuild required for " << path << ". Timestamps: " << modtime << " != " << path.modTime());
- if (delta < 30)
- {
- // File system time stamp has slightly changed. It's unlikely that the user has managed to change a file, start fgfs,
- // and then changed file again within x seconds - so it's suspicious...
- SG_LOG(SG_NAVCACHE, SG_ALERT, "NavCache: suspicious file timestamp change. Please report this to FlightGear. "
- << "Delta: " << delta << ", Timestamps: " << modtime << ", " << path.modTime() << ", path: " << path);
- }
}
else
{
d->transactionAborted = true;
}
-FGPositioned* NavDataCache::loadById(PositionedID rowid)
+FGPositionedRef NavDataCache::loadById(PositionedID rowid)
{
if (rowid == 0) {
return NULL;
{
// only runways are spatially indexed; don't bother indexing taxiways
// or pavements
- bool spatialIndex = (ty == FGPositioned::RUNWAY);
+ bool spatialIndex = ( ty == FGPositioned::RUNWAY || ty == FGPositioned::HELIPAD);
sqlite3_int64 rowId = d->insertPositioned(ty, cleanRunwayNo(ident), "", pos, apt,
spatialIndex);
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);
+ }
}
void NavDataCache::updateRunwayThreshold(PositionedID runwayID, const SGGeod &aThreshold,
sqlite3_bind_double(d->updateRunwayThreshold, 3, aDisplacedThreshold);
sqlite3_bind_double(d->updateRunwayThreshold, 4, aStopway);
d->execUpdate(d->updateRunwayThreshold);
-
-// compute the new runway center, based on the threshold lat/lon and length,
- double offsetFt = (0.5 * d->runwayLengthFt(runwayID));
- SGGeod newCenter;
- double dummy;
- SGGeodesy::direct(aThreshold, aHeading, offsetFt * SG_FEET_TO_METER, newCenter, dummy);
-
-// now update the positional data
- updatePosition(runwayID, newCenter);
+
+ // now update the positional data
+ updatePosition(runwayID, aThreshold);
}
PositionedID
sqlite3_bind_int(d->insertNavaid, 3, range);
sqlite3_bind_double(d->insertNavaid, 4, multiuse);
sqlite3_bind_int64(d->insertNavaid, 5, runway);
+ sqlite3_bind_int64(d->insertNavaid, 6, 0);
return d->execInsert(d->insertNavaid);
}
+void NavDataCache::setNavaidColocated(PositionedID navaid, PositionedID colocatedDME)
+{
+ // Update DB entries...
+ sqlite3_bind_int64(d->setNavaidColocated, 1, navaid);
+ sqlite3_bind_int64(d->setNavaidColocated, 2, colocatedDME);
+ d->execUpdate(d->setNavaidColocated);
+
+ // ...and the in-memory copy of the navrecord
+ if (d->cache.find(navaid) != d->cache.end()) {
+ FGNavRecord* rec = (FGNavRecord*) d->cache[navaid].get();
+ rec->setColocatedDME(colocatedDME);
+ }
+}
+
void NavDataCache::updateILS(PositionedID ils, const SGGeod& newPos, double aHdg)
{
sqlite3_bind_int64(d->updateILS, 1, ils);
return d->insertPositioned(FGPositioned::FIX, ident, string(), aPos, 0, true);
}
-PositionedID NavDataCache::createUserWaypoint(const std::string& ident, const SGGeod& aPos)
+PositionedID NavDataCache::createPOI(FGPositioned::Type ty, const std::string& ident, const SGGeod& aPos)
{
- return d->insertPositioned(FGPositioned::WAYPOINT, ident, string(), aPos, 0,
+ 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)
{
d->execUpdate(d->setAirportMetar);
}
-FGPositioned::List NavDataCache::findAllWithIdent(const string& s,
- FGPositioned::Filter* filter, bool exact)
+//------------------------------------------------------------------------------
+FGPositionedList NavDataCache::findAllWithIdent( const string& s,
+ FGPositioned::Filter* filter,
+ bool exact )
{
return d->findAllByString(s, "ident", filter, exact);
}
-FGPositioned::List NavDataCache::findAllWithName(const string& s,
- FGPositioned::Filter* filter, bool exact)
+//------------------------------------------------------------------------------
+FGPositionedList NavDataCache::findAllWithName( const string& s,
+ FGPositioned::Filter* filter,
+ bool exact )
{
return d->findAllByString(s, "name", filter, exact);
}
-
-FGPositionedRef NavDataCache::findClosestWithIdent(const string& aIdent,
- const SGGeod& aPos, FGPositioned::Filter* aFilter)
+
+//------------------------------------------------------------------------------
+FGPositionedRef NavDataCache::findClosestWithIdent( const string& aIdent,
+ const SGGeod& aPos,
+ FGPositioned::Filter* aFilter )
{
sqlite_bind_stdstring(d->findClosestWithIdent, 1, aIdent);
if (aFilter) {
sqlite3_bind_int64(q, 2, aAirport);
d->execUpdate(q);
- q = d->prepare("DELETE FROM taxi_node WHERE rowid IN (SELECT rowid FROM positioned WHERE type=?1 AND airport=?2)");
+ q = d->prepare("DELETE FROM taxi_node WHERE rowid IN (SELECT rowid FROM positioned WHERE (type=?1 OR type=?2) AND airport=?3)");
sqlite3_bind_int(q, 1, FGPositioned::TAXI_NODE);
- sqlite3_bind_int64(q, 2, aAirport);
+ sqlite3_bind_int(q, 2, FGPositioned::PARKING);
+ sqlite3_bind_int64(q, 3, aAirport);
d->execUpdate(q);
q = d->prepare("DELETE FROM positioned WHERE (type=?1 OR type=?2) AND airport=?3");