+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.
+ 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);
+
+ 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)
+{
+ 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);
+ 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)
+{
+ 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;
+ sqlite3_bind_int64(q, 1, aAirport);
+ return d->selectIds(q);
+}
+
+void NavDataCache::markGroundnetAsPushback(PositionedID nodeId)
+{
+ 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;
+ 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());
+
+ PositionedID result = 0;
+ while (d->execSelect(q)) {
+ PositionedID id = sqlite3_column_int64(q, 0);
+ if (!aRunway) {
+ result = id;
+ break;
+ }
+
+ // 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 ) {
+ result = id;
+ break;
+ }
+ }
+
+ d->reset(q);
+ return result;
+}
+
+PositionedIDVec NavDataCache::groundNetEdgesFrom(PositionedID pos, bool onlyPushback)
+{
+ sqlite3_stmt_ptr q = onlyPushback ? d->pushbackEdgesFrom : d->taxiEdgesFrom;
+ sqlite3_bind_int64(q, 1, pos);
+ return d->selectIds(q);
+}
+
+PositionedIDVec NavDataCache::findAirportParking(PositionedID airport, const std::string& flightType,
+ int radius)
+{
+ 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);
+}
+
+void NavDataCache::dropGroundnetFor(PositionedID aAirport)
+{
+ sqlite3_stmt_ptr q = d->prepare("DELETE FROM parking WHERE rowid IN (SELECT rowid FROM positioned WHERE type=?1 AND airport=?2)");
+ sqlite3_bind_int(q, 1, FGPositioned::PARKING);
+ 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 OR type=?2) AND airport=?3)");
+ sqlite3_bind_int(q, 1, FGPositioned::TAXI_NODE);
+ 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");
+ sqlite3_bind_int(q, 1, FGPositioned::TAXI_NODE);
+ sqlite3_bind_int(q, 2, FGPositioned::PARKING);
+ sqlite3_bind_int64(q, 3, aAirport);
+ d->execUpdate(q);
+
+ q = d->prepare("DELETE FROM groundnet_edge WHERE airport=?1");
+ sqlite3_bind_int64(q, 1, aAirport);
+ d->execUpdate(q);
+}
+
+void NavDataCache::dropAllGroundnets()
+{
+ SG_LOG(SG_NAVCACHE, SG_INFO, "dropping ground-net data");
+ beginTransaction();
+ d->runSQL("DELETE FROM groundnet_edge");
+ d->runSQL("DELETE FROM parking");
+ d->runSQL("DELETE FROM taxi_node");
+
+ sqlite3_stmt_ptr q = d->prepare("DELETE FROM positioned WHERE (type=?1 OR type=?2)");
+ sqlite3_bind_int(q, 1, FGPositioned::TAXI_NODE);
+ sqlite3_bind_int(q, 2, FGPositioned::PARKING);
+ d->execUpdate(q);
+ commitTransaction();
+}
+
+bool NavDataCache::isReadOnly() const
+{
+ return d->readOnly;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Transaction RAII object
+
+NavDataCache::Transaction::Transaction(NavDataCache* cache) :
+ _instance(cache),
+ _committed(false)
+{
+ assert(cache);
+ _instance->beginTransaction();
+}
+
+NavDataCache::Transaction::~Transaction()
+{
+ if (!_committed) {
+ SG_LOG(SG_NAVCACHE, SG_INFO, "aborting cache transaction!");
+ _instance->abortTransaction();
+ }
+}
+
+void NavDataCache::Transaction::commit()
+{
+ assert(!_committed);
+ _committed = true;
+ _instance->commitTransaction();
+}
+