#include "PositionedOctree.hxx"
#include <Airports/apt_loader.hxx>
#include <Navaids/airways.hxx>
+#include <Airports/parking.hxx>
+#include <Airports/gnnode.hxx>
using std::string;
namespace {
-const int SCHEMA_VERSION = 4;
+const int SCHEMA_VERSION = 5;
// 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
try {
execSelect(stmt);
- } catch (sg_exception& e) {
+ } catch (sg_exception&) {
sqlite3_finalize(stmt);
throw; // re-throw
}
")");
runSQL("CREATE INDEX airway_edge_from ON airway_edge(a)");
+
+ runSQL("CREATE TABLE taxi_node ("
+ "hold_type INT,"
+ "on_runway BOOL,"
+ "pushback BOOL"
+ ")");
+
+ runSQL("CREATE TABLE parking ("
+ "heading FLOAT,"
+ "radius INT,"
+ "gate_type VARCHAR,"
+ "airlines VARCHAR,"
+ "pushback INT64"
+ ")");
+
+ runSQL("CREATE TABLE groundnet_edge ("
+ "airport INT64,"
+ "a INT64,"
+ "b INT64"
+ ")");
+
+ runSQL("CREATE INDEX groundnet_edge_airport ON groundnet_edge(airport)");
+ runSQL("CREATE INDEX groundnet_edge_from ON groundnet_edge(a)");
}
void prepareQueries()
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");
sqlite3_bind_int(findILS, 4, FGPositioned::ILS);
sqlite3_bind_int(findILS, 5, FGPositioned::LOC);
+ // airways
findAirway = prepare("SELECT rowid FROM airway WHERE network=?1 AND ident=?2");
insertAirway = prepare("INSERT INTO airway (ident, network) "
"VALUES (?1, ?2)");
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");
+
+ // parking / taxi-node graph
+ insertTaxiNode = prepare("INSERT INTO taxi_node (rowid, hold_type, on_runway, pushback) VALUES(?1, ?2, ?3, 0)");
+ insertParkingPos = prepare("INSERT INTO parking (rowid, heading, radius, gate_type, airlines) "
+ "VALUES (?1, ?2, ?3, ?4, ?5)");
+ setParkingPushBack = prepare("UPDATE parking SET pushback=?2 WHERE rowid=?1");
+
+ loadTaxiNodeStmt = prepare("SELECT hold_type, on_runway FROM taxi_node WHERE rowid=?1");
+ loadParkingPos = prepare("SELECT heading, radius, gate_type, airlines, pushback FROM parking WHERE rowid=?1");
+ taxiEdgesFrom = prepare("SELECT b FROM groundnet_edge WHERE a=?1");
+ pushbackEdgesFrom = prepare("SELECT b FROM groundnet_edge, taxi_node WHERE "
+ "a=?1 AND groundnet_edge.b = taxi_node.rowid AND pushback=1");
+
+ insertTaxiEdge = prepare("INSERT INTO groundnet_edge (airport, a,b) VALUES(?1, ?2, ?3)");
+
+ markTaxiNodeAsPushback = prepare("UPDATE taxi_node SET pushback=1 WHERE rowid=?1");
+ airportTaxiNodes = prepare("SELECT rowid FROM positioned WHERE (type=?2 OR type=?3) AND airport=?1");
+ sqlite3_bind_int(airportTaxiNodes, 2, FGPositioned::PARKING);
+ sqlite3_bind_int(airportTaxiNodes, 3, FGPositioned::TAXI_NODE);
+
+ airportPushbackNodes = prepare("SELECT positioned.rowid FROM positioned, taxi_node WHERE "\
+ "airport=?1 AND positioned.rowid=taxi_node.rowid AND pushback=1 "
+ "AND (type=?2 OR type=?3)");
+ sqlite3_bind_int(airportPushbackNodes, 2, FGPositioned::PARKING);
+ sqlite3_bind_int(airportPushbackNodes, 3, FGPositioned::TAXI_NODE);
+
+ findNearestTaxiNode = prepare("SELECT positioned.rowid FROM positioned, taxi_node WHERE "
+ "positioned.rowid = taxi_node.rowid AND airport=?1 "
+ "ORDER BY distanceCartSqr(cart_x, cart_y, cart_z, ?2, ?3, ?4) "
+ "LIMIT 1");
+
+ findNearestRunwayTaxiNode = prepare("SELECT positioned.rowid FROM positioned, taxi_node WHERE "
+ "positioned.rowid = taxi_node.rowid AND airport=?1 "
+ "AND on_runway=1 "
+ "ORDER BY distanceCartSqr(cart_x, cart_y, cart_z, ?2, ?3, ?4) ");
+
+ findAirportParking = prepare("SELECT positioned.rowid FROM positioned, parking WHERE "
+ "airport=?1 AND type=?4 AND "
+ "radius >= ?2 AND gate_type = ?3 AND "
+ "parking.rowid=positioned.rowid");
+ sqlite3_bind_int(findAirportParking, 4, FGPositioned::PARKING);
}
void writeIntProperty(const string& key, int value)
reset(loadAirportStmt);
sqlite3_bind_int64(loadAirportStmt, 1, rowId);
execSelect1(loadAirportStmt);
- bool hasMetar = sqlite3_column_int(loadAirportStmt, 0);
+ bool hasMetar = (sqlite3_column_int(loadAirportStmt, 0) > 0);
return new FGAirport(rowId, id, pos, name, hasMetar, ty);
}
int range = sqlite3_column_int(loadCommStation, 0);
int freqKhz = sqlite3_column_int(loadCommStation, 1);
- CommStation* c = new CommStation(rowId, id, ty, pos, freqKhz, range);
+ CommStation* c = new CommStation(rowId, name, ty, pos, freqKhz, range);
c->setAirport(airport);
return c;
}
return new FGNavRecord(rowId, ty, id, name, pos, freq, rangeNm, mulituse, runway);
}
+ FGPositioned* loadParking(sqlite3_int64 rowId,
+ const string& name, const SGGeod& pos,
+ PositionedID airport)
+ {
+ reset(loadParkingPos);
+ sqlite3_bind_int64(loadParkingPos, 1, rowId);
+ execSelect1(loadParkingPos);
+
+ double heading = sqlite3_column_double(loadParkingPos, 0);
+ int radius = sqlite3_column_int(loadParkingPos, 1);
+ string aircraftType((char*) sqlite3_column_text(loadParkingPos, 2));
+ string airlines((char*) sqlite3_column_text(loadParkingPos, 3));
+ PositionedID pushBack = sqlite3_column_int64(loadParkingPos, 4);
+
+ return new FGParking(rowId, pos, heading, radius, name, aircraftType, airlines, pushBack);
+ }
+
+ FGPositioned* loadTaxiNode(sqlite3_int64 rowId, const SGGeod& pos,
+ PositionedID airport)
+ {
+ reset(loadTaxiNodeStmt);
+ sqlite3_bind_int64(loadTaxiNodeStmt, 1, rowId);
+ execSelect1(loadTaxiNodeStmt);
+
+ int hold_type = sqlite3_column_int(loadTaxiNodeStmt, 0);
+ bool onRunway = sqlite3_column_int(loadTaxiNodeStmt, 1);
+ return new FGTaxiNode(rowId, pos, onRunway, hold_type);
+ }
+
PositionedID insertPositioned(FGPositioned::Type ty, const string& ident,
const string& name, const SGGeod& pos, PositionedID apt,
bool spatialIndex)
FGPositioned::Filter* filter, bool exact)
{
string query = s;
- if (!exact) query += "*";
+ if (!exact) query += "%";
// build up SQL query text
string matchTerm = exact ? "=?1" : " LIKE ?1";
string sql = "SELECT rowid FROM positioned WHERE " + column + matchTerm;
if (filter) {
- sql += AND_TYPED;
+ sql += " " AND_TYPED;
}
// find or prepare a suitable statement frrm the SQL
sqlite3_stmt_ptr searchAirports;
sqlite3_stmt_ptr findCommByFreq, findNavsByFreq,
- findNavsByFreqNoPos;
+ findNavsByFreqNoPos, findNavaidForRunway;
sqlite3_stmt_ptr getAirportItems, getAirportItemByIdent;
sqlite3_stmt_ptr findAirportRunway,
findILS;
sqlite3_stmt_ptr findAirway, insertAirwayEdge, isPosInAirway, airwayEdgesFrom,
insertAirway;
+// groundnet (parking, taxi node graph)
+ sqlite3_stmt_ptr loadTaxiNodeStmt, loadParkingPos, insertTaxiNode, insertParkingPos;
+ sqlite3_stmt_ptr taxiEdgesFrom, pushbackEdgesFrom, insertTaxiEdge, markTaxiNodeAsPushback,
+ airportTaxiNodes, airportPushbackNodes, findNearestTaxiNode, findAirportParking,
+ setParkingPushBack, findNearestRunwayTaxiNode;
+
// since there's many permutations of ident/name queries, we create
// them programtically, but cache the exact query by its raw SQL once
// used.
case FGPositioned::FREQ_UNICOM:
return loadComm(rowid, ty, ident, name, pos, aptId);
+ case FGPositioned::TAXI_NODE:
+ return loadTaxiNode(rowid, pos, aptId);
+
+ case FGPositioned::PARKING:
+ return loadParking(rowid, ident, pos, aptId);
+
default:
return NULL;
}
// reached this point with no exception, success
break;
} catch (sg_exception& e) {
- SG_LOG(SG_NAVCACHE, SG_WARN, "NavCache: init failed:" << e.what()
+ SG_LOG(SG_NAVCACHE, t == 0 ? SG_WARN : SG_ALERT, "NavCache: init failed:" << e.what()
<< " (attempt " << t << ")");
- homePath.remove();
d.reset();
+ homePath.remove();
}
} // of retry loop
d->execInsert(d->stampFileCache);
}
+void NavDataCache::beginTransaction()
+{
+ d->runSQL("BEGIN");
+}
+
+void NavDataCache::commitTransaction()
+{
+ d->runSQL("COMMIT");
+}
+
+void NavDataCache::abortTransaction()
+{
+ d->runSQL("ROLLBACK");
+}
FGPositioned* NavDataCache::loadById(PositionedID rowid)
{
void NavDataCache::updatePosition(PositionedID item, const SGGeod &pos)
{
+ if (d->cache.find(item) != d->cache.end()) {
+ SG_LOG(SG_GENERAL, SG_DEBUG, "updating position of an item in the cache");
+ d->cache[item]->modifyPosition(pos);
+ }
+
SGVec3d cartPos(SGVec3d::fromGeod(pos));
d->reset(d->setAirportPos);
sqlite3_bind_double(d->findCommByFreq, 5, cartPos.y());
sqlite3_bind_double(d->findCommByFreq, 6, cartPos.z());
- if (!d->execSelect(d->findCommByFreq)) {
- return NULL;
+ while (d->execSelect(d->findCommByFreq)) {
+ FGPositioned* p = loadById(sqlite3_column_int64(d->findCommByFreq, 0));
+ if (aFilter && !aFilter->pass(p)) {
+ continue;
+ }
+
+ return p;
}
- return loadById(sqlite3_column_int64(d->findCommByFreq, 0));
+ return NULL;
}
PositionedIDVec
}
return result;
}
+
+PositionedID NavDataCache::findNavaidForRunway(PositionedID runway, FGPositioned::Type ty)
+{
+ d->reset(d->findNavaidForRunway);
+ sqlite3_bind_int64(d->findNavaidForRunway, 1, runway);
+ sqlite3_bind_int(d->findNavaidForRunway, 2, ty);
+ if (!d->execSelect(d->findNavaidForRunway)) {
+ return 0;
+ }
+
+ return sqlite3_column_int64(d->findNavaidForRunway, 0);
+}
+
+PositionedID
+NavDataCache::insertParking(const std::string& name, const SGGeod& aPos,
+ PositionedID aAirport,
+ double aHeading, int aRadius, const std::string& aAircraftType,
+ const std::string& aAirlines)
+{
+ sqlite3_int64 rowId = d->insertPositioned(FGPositioned::PARKING, name, "", aPos, aAirport, false);
+
+// we need to insert a row into the taxi_node table, otherwise we can't maintain
+// the appropriate pushback flag.
+ d->reset(d->insertTaxiNode);
+ sqlite3_bind_int64(d->insertTaxiNode, 1, rowId);
+ sqlite3_bind_int(d->insertTaxiNode, 2, 0);
+ sqlite3_bind_int(d->insertTaxiNode, 3, 0);
+ d->execInsert(d->insertTaxiNode);
+
+ d->reset(d->insertParkingPos);
+ sqlite3_bind_int64(d->insertParkingPos, 1, rowId);
+ sqlite3_bind_double(d->insertParkingPos, 2, aHeading);
+ sqlite3_bind_int(d->insertParkingPos, 3, aRadius);
+ sqlite_bind_stdstring(d->insertParkingPos, 4, aAircraftType);
+ sqlite_bind_stdstring(d->insertParkingPos, 5, aAirlines);
+ return d->execInsert(d->insertParkingPos);
+}
+
+void NavDataCache::setParkingPushBackRoute(PositionedID parking, PositionedID pushBackNode)
+{
+ d->reset(d->setParkingPushBack);
+ sqlite3_bind_int64(d->setParkingPushBack, 1, parking);
+ sqlite3_bind_int64(d->setParkingPushBack, 2, pushBackNode);
+ d->execUpdate(d->setParkingPushBack);
+}
+
+PositionedID
+NavDataCache::insertTaxiNode(const SGGeod& aPos, PositionedID aAirport, int aHoldType, bool aOnRunway)
+{
+ sqlite3_int64 rowId = d->insertPositioned(FGPositioned::TAXI_NODE, string(), string(), aPos, aAirport, false);
+ d->reset(d->insertTaxiNode);
+ sqlite3_bind_int64(d->insertTaxiNode, 1, rowId);
+ sqlite3_bind_int(d->insertTaxiNode, 2, aHoldType);
+ sqlite3_bind_int(d->insertTaxiNode, 3, aOnRunway);
+ return d->execInsert(d->insertTaxiNode);
+}
+
+void NavDataCache::insertGroundnetEdge(PositionedID aAirport, PositionedID from, PositionedID to)
+{
+ d->reset(d->insertTaxiEdge);
+ sqlite3_bind_int64(d->insertTaxiEdge, 1, aAirport);
+ sqlite3_bind_int64(d->insertTaxiEdge, 2, from);
+ sqlite3_bind_int64(d->insertTaxiEdge, 3, to);
+ d->execInsert(d->insertTaxiEdge);
+}
+
+PositionedIDVec NavDataCache::groundNetNodes(PositionedID aAirport, bool onlyPushback)
+{
+ sqlite3_stmt_ptr q = onlyPushback ? d->airportPushbackNodes : d->airportTaxiNodes;
+ d->reset(q);
+ sqlite3_bind_int64(q, 1, aAirport);
+ return d->selectIds(q);
+}
+void NavDataCache::markGroundnetAsPushback(PositionedID nodeId)
+{
+ d->reset(d->markTaxiNodeAsPushback);
+ sqlite3_bind_int64(d->markTaxiNodeAsPushback, 1, nodeId);
+ d->execUpdate(d->markTaxiNodeAsPushback);
+}
+
+static double headingDifferenceDeg(double crs1, double crs2)
+{
+ double diff = crs2 - crs1;
+ SG_NORMALIZE_RANGE(diff, -180.0, 180.0);
+ return diff;
+}
+
+PositionedID NavDataCache::findGroundNetNode(PositionedID airport, const SGGeod& aPos,
+ bool onRunway, FGRunway* aRunway)
+{
+ sqlite3_stmt_ptr q = onRunway ? d->findNearestRunwayTaxiNode : d->findNearestTaxiNode;
+ d->reset(q);
+ sqlite3_bind_int64(q, 1, airport);
+
+ SGVec3d cartPos(SGVec3d::fromGeod(aPos));
+ sqlite3_bind_double(q, 2, cartPos.x());
+ sqlite3_bind_double(q, 3, cartPos.y());
+ sqlite3_bind_double(q, 4, cartPos.z());
+
+ while (d->execSelect(q)) {
+ PositionedID id = sqlite3_column_int64(q, 0);
+ if (!aRunway) {
+ return id;
+ }
+
+ // ensure found node lies on the runway
+ FGPositionedRef node = loadById(id);
+ double course = SGGeodesy::courseDeg(node->geod(), aRunway->end());
+ if (fabs(headingDifferenceDeg(course, aRunway->headingDeg())) < 3.0 ) {
+ return id;
+ }
+ }
+
+ return 0;
+}
+
+PositionedIDVec NavDataCache::groundNetEdgesFrom(PositionedID pos, bool onlyPushback)
+{
+ sqlite3_stmt_ptr q = onlyPushback ? d->pushbackEdgesFrom : d->taxiEdgesFrom;
+ d->reset(q);
+ sqlite3_bind_int64(q, 1, pos);
+ return d->selectIds(q);
+}
+
+PositionedIDVec NavDataCache::findAirportParking(PositionedID airport, const std::string& flightType,
+ int radius)
+{
+ d->reset(d->findAirportParking);
+ sqlite3_bind_int64(d->findAirportParking, 1, airport);
+ sqlite3_bind_int(d->findAirportParking, 2, radius);
+ sqlite_bind_stdstring(d->findAirportParking, 3, flightType);
+
+ return d->selectIds(d->findAirportParking);
+}
+
} // of namespace flightgear