Extend the waypoint / POI nav-data types
- add new VRP type - add the concept of temporary waypoints / POIs which are not stored in the persisten cache - add Nasal APIs to create/delete/move waypoints, in addition to the existing create-waypoint and delete-waypoint commands - allow POIs/waypoints to have names as well as idents - Ensure ident/name and spatial queries work correctly for temporary and dynamic waypoints - add test coverage for all this
This commit is contained in:
parent
c456d18643
commit
6d70f48f73
21 changed files with 666 additions and 286 deletions
|
@ -16,7 +16,7 @@ using namespace flightgear;
|
||||||
|
|
||||||
FGTaxiNode::FGTaxiNode(FGPositioned::Type ty, int index, const SGGeod& pos,
|
FGTaxiNode::FGTaxiNode(FGPositioned::Type ty, int index, const SGGeod& pos,
|
||||||
bool aOnRunway, int aHoldType,
|
bool aOnRunway, int aHoldType,
|
||||||
const std::string& ident) : FGPositioned(TRANSIENT_ID, ty, ident, pos),
|
const std::string& ident) : FGPositioned(NavDataCache::instance()->createTransientID(), ty, ident, pos),
|
||||||
m_index(index),
|
m_index(index),
|
||||||
isOnRunway(aOnRunway),
|
isOnRunway(aOnRunway),
|
||||||
holdType(aHoldType),
|
holdType(aHoldType),
|
||||||
|
|
|
@ -1333,9 +1333,12 @@ bool FGRouteMgr::commandDefineUserWaypoint(const SGPropertyNode * arg, SGPropert
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bool temporary = arg->getBoolValue("temporary");
|
||||||
SGGeod pos(SGGeod::fromDeg(arg->getDoubleValue("longitude-deg"),
|
SGGeod pos(SGGeod::fromDeg(arg->getDoubleValue("longitude-deg"),
|
||||||
arg->getDoubleValue("latitude-deg")));
|
arg->getDoubleValue("latitude-deg")));
|
||||||
FGPositioned::createUserWaypoint(ident, pos);
|
const auto name = arg->getStringValue("name");
|
||||||
|
FGPositioned::createWaypoint(FGPositioned::WAYPOINT, ident, pos,
|
||||||
|
temporary, name);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1347,7 +1350,14 @@ bool FGRouteMgr::commandDeleteUserWaypoint(const SGPropertyNode * arg, SGPropert
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return FGPositioned::deleteUserWaypoint(ident);
|
FGPositioned::TypeFilter f(FGPositioned::WAYPOINT);
|
||||||
|
auto existing = FGPositioned::findFirstWithIdent(ident, &f);
|
||||||
|
if (!existing) {
|
||||||
|
SG_LOG(SG_AUTOPILOT, SG_WARN, "no user waypoint with ident:" << ident);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FGPositioned::deleteWaypoint(existing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1519,7 +1519,7 @@ void GPS::defineWaypoint()
|
||||||
}
|
}
|
||||||
|
|
||||||
SG_LOG(SG_INSTR, SG_INFO, "GPS:defineWaypoint: creating waypoint:" << ident);
|
SG_LOG(SG_INSTR, SG_INFO, "GPS:defineWaypoint: creating waypoint:" << ident);
|
||||||
FGPositionedRef wpt = FGPositioned::createUserWaypoint(ident, _scratchPos);
|
FGPositionedRef wpt = FGPositioned::createWaypoint(FGPositioned::WAYPOINT, ident, _scratchPos, false);
|
||||||
_searchResults.clear();
|
_searchResults.clear();
|
||||||
_searchResults.push_back(wpt);
|
_searchResults.push_back(wpt);
|
||||||
setScratchFromPositioned(wpt.get(), -1);
|
setScratchFromPositioned(wpt.get(), -1);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
const int SCHEMA_VERSION = 22;
|
const int SCHEMA_VERSION = 25;
|
||||||
|
|
||||||
#define SCHEMA_SQL \
|
#define SCHEMA_SQL \
|
||||||
"CREATE TABLE properties (key VARCHAR, value VARCHAR);" \
|
"CREATE TABLE properties (key VARCHAR, value VARCHAR);" \
|
||||||
|
@ -32,3 +32,18 @@ const int SCHEMA_VERSION = 22;
|
||||||
"CREATE TABLE airway_edge (network INT,airway INT64,a INT64,b INT64);" \
|
"CREATE TABLE airway_edge (network INT,airway INT64,a INT64,b INT64);" \
|
||||||
"CREATE INDEX airway_edge_from ON airway_edge(a);" \
|
"CREATE INDEX airway_edge_from ON airway_edge(a);" \
|
||||||
"CREATE INDEX airway_edge_to ON airway_edge(b);"
|
"CREATE INDEX airway_edge_to ON airway_edge(b);"
|
||||||
|
|
||||||
|
// this part is run for every new connection, including read-only connections
|
||||||
|
|
||||||
|
#define TEMPORARY_SCHEMA_SQL \
|
||||||
|
"CREATE TEMPORARY TABLE temp_positioned (type INT, ident VARCHAR collate nocase," \
|
||||||
|
"name VARCHAR collate nocase, airport INT64, lon FLOAT, lat FLOAT," \
|
||||||
|
"elev_m FLOAT, octree_node INT, cart_x FLOAT, cart_y FLOAT, cart_z FLOAT);" \
|
||||||
|
\
|
||||||
|
\
|
||||||
|
"CREATE TEMPORARY VIEW all_positioned AS " \
|
||||||
|
"SELECT rowid AS guid, type, ident, name, airport, lon, lat," \
|
||||||
|
"elev_m, octree_node, cart_x, cart_y, cart_z FROM positioned " \
|
||||||
|
"UNION ALL " \
|
||||||
|
"SELECT rowid AS guid, type, ident, name, airport, lon, lat," \
|
||||||
|
"elev_m, octree_node, cart_x, cart_y, cart_z FROM temp_positioned;"
|
||||||
|
|
|
@ -329,6 +329,8 @@ public:
|
||||||
|
|
||||||
reset(checkTables);
|
reset(checkTables);
|
||||||
|
|
||||||
|
initTemporaryTables();
|
||||||
|
|
||||||
readPropertyQuery = prepare("SELECT value FROM properties WHERE key=?");
|
readPropertyQuery = prepare("SELECT value FROM properties WHERE key=?");
|
||||||
writePropertyQuery = prepare("INSERT INTO properties (key, value) VALUES (?,?)");
|
writePropertyQuery = prepare("INSERT INTO properties (key, value) VALUES (?,?)");
|
||||||
clearProperty = prepare("DELETE FROM properties WHERE key=?1");
|
clearProperty = prepare("DELETE FROM properties WHERE key=?1");
|
||||||
|
@ -549,6 +551,18 @@ public:
|
||||||
} // of commands in scheme loop
|
} // of commands in scheme loop
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void initTemporaryTables()
|
||||||
|
{
|
||||||
|
const auto commands = simgear::strutils::split(TEMPORARY_SCHEMA_SQL, ";");
|
||||||
|
for (auto sql : commands) {
|
||||||
|
if (sql.empty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
runSQL(sql);
|
||||||
|
} // of commands in scheme loop
|
||||||
|
}
|
||||||
|
|
||||||
void prepareQueries()
|
void prepareQueries()
|
||||||
{
|
{
|
||||||
writePropertyMulti = prepare("INSERT INTO properties (key, value) VALUES(?1,?2)");
|
writePropertyMulti = prepare("INSERT INTO properties (key, value) VALUES(?1,?2)");
|
||||||
|
@ -558,20 +572,20 @@ public:
|
||||||
rollbackTransactionStmt = prepare("ROLLBACK");
|
rollbackTransactionStmt = prepare("ROLLBACK");
|
||||||
|
|
||||||
|
|
||||||
#define POSITIONED_COLS "rowid, type, ident, name, airport, lon, lat, elev_m, octree_node"
|
#define POSITIONED_COLS "guid, type, ident, name, airport, lon, lat, elev_m, octree_node"
|
||||||
#define AND_TYPED "AND type>=?2 AND type <=?3"
|
#define AND_TYPED "AND type>=?2 AND type <=?3"
|
||||||
statCacheCheck = prepare("SELECT stamp, sha FROM stat_cache WHERE path=?");
|
statCacheCheck = prepare("SELECT stamp, sha FROM stat_cache WHERE path=?");
|
||||||
stampFileCache = prepare("INSERT OR REPLACE INTO stat_cache "
|
stampFileCache = prepare("INSERT OR REPLACE INTO stat_cache "
|
||||||
"(path, stamp, sha) VALUES (?,?, ?)");
|
"(path, stamp, sha) VALUES (?,?, ?)");
|
||||||
|
|
||||||
loadPositioned = prepare("SELECT " POSITIONED_COLS " FROM positioned WHERE rowid=?");
|
loadPositioned = prepare("SELECT " POSITIONED_COLS " FROM all_positioned WHERE guid=?");
|
||||||
loadAirportStmt = prepare("SELECT scenery_path, has_metar FROM airport WHERE rowid=?");
|
loadAirportStmt = prepare("SELECT scenery_path, has_metar FROM airport WHERE rowid=?");
|
||||||
loadNavaid = prepare("SELECT range_nm, freq, multiuse, runway, colocated FROM navaid 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=?");
|
loadCommStation = prepare("SELECT freq_khz, range_nm FROM comm WHERE rowid=?");
|
||||||
loadRunwayStmt = prepare("SELECT heading, length_ft, width_m, surface, displaced_threshold,"
|
loadRunwayStmt = prepare("SELECT heading, length_ft, width_m, surface, displaced_threshold,"
|
||||||
"stopway, reciprocal, ils FROM runway WHERE rowid=?1");
|
"stopway, reciprocal, ils FROM runway WHERE rowid=?1");
|
||||||
|
|
||||||
getAirportItems = prepare("SELECT rowid FROM positioned WHERE airport=?1 " AND_TYPED);
|
getAirportItems = prepare("SELECT guid FROM all_positioned WHERE airport=?1 " AND_TYPED);
|
||||||
|
|
||||||
|
|
||||||
setAirportMetar = prepare("UPDATE airport SET has_metar=?2 WHERE rowid="
|
setAirportMetar = prepare("UPDATE airport SET has_metar=?2 WHERE rowid="
|
||||||
|
@ -588,8 +602,16 @@ public:
|
||||||
"cart_x, cart_y, cart_z)"
|
"cart_x, cart_y, cart_z)"
|
||||||
" VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11)");
|
" 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, "
|
insertTempPosQuery = prepare("INSERT INTO temp_positioned "
|
||||||
|
"(rowid, type, ident, name, lon, lat, elev_m, "
|
||||||
|
"cart_x, cart_y, cart_z)"
|
||||||
|
" VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10)");
|
||||||
|
|
||||||
|
updatePosition = 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");
|
"cart_x=?6, cart_y=?7, cart_z=?8 WHERE rowid=?1");
|
||||||
|
updateTempPos = prepare("UPDATE temp_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, scenery_path, has_metar) VALUES (?, ?, ?)");
|
insertAirport = prepare("INSERT INTO airport (rowid, scenery_path, has_metar) VALUES (?, ?, ?)");
|
||||||
insertNavaid = prepare("INSERT INTO navaid (rowid, freq, range_nm, multiuse, runway, colocated)"
|
insertNavaid = prepare("INSERT INTO navaid (rowid, freq, range_nm, multiuse, runway, colocated)"
|
||||||
" VALUES (?1, ?2, ?3, ?4, ?5, ?6)");
|
" VALUES (?1, ?2, ?3, ?4, ?5, ?6)");
|
||||||
|
@ -601,11 +623,11 @@ public:
|
||||||
" VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)");
|
" VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)");
|
||||||
runwayLengthFtQuery = prepare("SELECT length_ft FROM runway WHERE rowid=?1");
|
runwayLengthFtQuery = prepare("SELECT length_ft FROM runway WHERE rowid=?1");
|
||||||
|
|
||||||
removePOIQuery = prepare("DELETE FROM positioned WHERE type=?1 AND ident=?2");
|
removePositionedQuery = prepare("DELETE FROM positioned WHERE rowid=?1");
|
||||||
|
removeTempPosQuery = prepare("DELETE FROM temp_positioned WHERE rowid=?1");
|
||||||
|
|
||||||
// query statement
|
// query statement
|
||||||
findClosestWithIdent = prepare("SELECT rowid FROM positioned WHERE ident=?1 "
|
findClosestWithIdent = prepare("SELECT guid FROM all_positioned WHERE ident=?1 " AND_TYPED " ORDER BY distanceCartSqr(cart_x, cart_y, cart_z, ?4, ?5, ?6)");
|
||||||
AND_TYPED " ORDER BY distanceCartSqr(cart_x, cart_y, cart_z, ?4, ?5, ?6)");
|
|
||||||
|
|
||||||
findCommByFreq = prepare("SELECT positioned.rowid FROM positioned, comm WHERE "
|
findCommByFreq = prepare("SELECT positioned.rowid FROM positioned, comm WHERE "
|
||||||
"positioned.rowid=comm.rowid AND freq_khz=?1 "
|
"positioned.rowid=comm.rowid AND freq_khz=?1 "
|
||||||
|
@ -649,7 +671,7 @@ public:
|
||||||
sqlite3_bind_int(getAllAirports, 2, FGPositioned::SEAPORT);
|
sqlite3_bind_int(getAllAirports, 2, FGPositioned::SEAPORT);
|
||||||
|
|
||||||
|
|
||||||
getAirportItemByIdent = prepare("SELECT rowid FROM positioned WHERE airport=?1 AND ident=?2 AND type=?3");
|
getAirportItemByIdent = prepare("SELECT guid FROM all_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="
|
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)");
|
"(SELECT rowid FROM positioned WHERE type=?4 AND ident=?1)");
|
||||||
|
@ -839,6 +861,36 @@ public:
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PositionedID insertTemporaryPositioned(PositionedID guid, FGPositioned::Type ty, const string& ident,
|
||||||
|
const string& name, const SGGeod& pos,
|
||||||
|
bool spatialIndex)
|
||||||
|
{
|
||||||
|
if (abandonCache)
|
||||||
|
throw AbandonCacheException{};
|
||||||
|
|
||||||
|
SGVec3d cartPos(SGVec3d::fromGeod(pos));
|
||||||
|
|
||||||
|
sqlite3_bind_int64(insertTempPosQuery, 1, guid);
|
||||||
|
sqlite3_bind_int(insertTempPosQuery, 2, ty);
|
||||||
|
sqlite_bind_stdstring(insertTempPosQuery, 3, ident);
|
||||||
|
sqlite_bind_stdstring(insertTempPosQuery, 4, name);
|
||||||
|
sqlite3_bind_double(insertTempPosQuery, 5, pos.getLongitudeDeg());
|
||||||
|
sqlite3_bind_double(insertTempPosQuery, 6, pos.getLatitudeDeg());
|
||||||
|
sqlite3_bind_double(insertTempPosQuery, 7, pos.getElevationM());
|
||||||
|
sqlite3_bind_double(insertTempPosQuery, 8, cartPos.x());
|
||||||
|
sqlite3_bind_double(insertTempPosQuery, 9, cartPos.y());
|
||||||
|
sqlite3_bind_double(insertTempPosQuery, 10, cartPos.z());
|
||||||
|
|
||||||
|
execInsert(insertTempPosQuery);
|
||||||
|
|
||||||
|
// insert into the transient octree
|
||||||
|
Octree::Leaf* octreeLeaf = Octree::globalTransientOctree()->findLeafForPos(cartPos);
|
||||||
|
octreeLeaf->insertChild(ty, guid);
|
||||||
|
|
||||||
|
return guid;
|
||||||
|
}
|
||||||
|
|
||||||
FGPositionedList findAllByString(const string& s, const string& column,
|
FGPositionedList findAllByString(const string& s, const string& column,
|
||||||
FGPositioned::Filter* filter, bool exact)
|
FGPositioned::Filter* filter, bool exact)
|
||||||
{
|
{
|
||||||
|
@ -847,7 +899,7 @@ public:
|
||||||
|
|
||||||
// build up SQL query text
|
// build up SQL query text
|
||||||
string matchTerm = exact ? "=?1" : " LIKE ?1";
|
string matchTerm = exact ? "=?1" : " LIKE ?1";
|
||||||
string sql = "SELECT rowid FROM positioned WHERE " + column + matchTerm;
|
string sql = "SELECT guid FROM all_positioned WHERE " + column + matchTerm;
|
||||||
if (filter) {
|
if (filter) {
|
||||||
sql += " " AND_TYPED;
|
sql += " " AND_TYPED;
|
||||||
}
|
}
|
||||||
|
@ -916,12 +968,12 @@ public:
|
||||||
deferredOctreeUpdates.clear();
|
deferredOctreeUpdates.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void removePositionedWithIdent(FGPositioned::Type ty, const std::string& aIdent)
|
void removePositioned(PositionedID rowid)
|
||||||
{
|
{
|
||||||
sqlite3_bind_int(removePOIQuery, 1, ty);
|
auto stmt = rowid < 0 ? removeTempPosQuery : removePositionedQuery;
|
||||||
sqlite_bind_stdstring(removePOIQuery, 2, aIdent);
|
sqlite3_bind_int64(stmt, 1, rowid);
|
||||||
execUpdate(removePOIQuery);
|
execUpdate(stmt);
|
||||||
reset(removePOIQuery);
|
reset(stmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
NavDataCache* outer;
|
NavDataCache* outer;
|
||||||
|
@ -959,9 +1011,10 @@ public:
|
||||||
|
|
||||||
sqlite3_stmt_ptr insertPositionedQuery, insertAirport, insertTower, insertRunway,
|
sqlite3_stmt_ptr insertPositionedQuery, insertAirport, insertTower, insertRunway,
|
||||||
insertCommStation, insertNavaid;
|
insertCommStation, insertNavaid;
|
||||||
|
sqlite3_stmt_ptr insertTempPosQuery;
|
||||||
sqlite3_stmt_ptr setAirportMetar, setRunwayReciprocal, setRunwayILS, setNavaidColocated,
|
sqlite3_stmt_ptr setAirportMetar, setRunwayReciprocal, setRunwayILS, setNavaidColocated,
|
||||||
setAirportPos;
|
updatePosition, updateTempPos;
|
||||||
sqlite3_stmt_ptr removePOIQuery;
|
sqlite3_stmt_ptr removePositionedQuery, removeTempPosQuery;
|
||||||
|
|
||||||
sqlite3_stmt_ptr findClosestWithIdent;
|
sqlite3_stmt_ptr findClosestWithIdent;
|
||||||
// octree (spatial index) related queries
|
// octree (spatial index) related queries
|
||||||
|
@ -996,6 +1049,10 @@ public:
|
||||||
// if we're performing a rebuild, the thread that is doing the work.
|
// if we're performing a rebuild, the thread that is doing the work.
|
||||||
// otherwise, NULL
|
// otherwise, NULL
|
||||||
std::unique_ptr<RebuildThread> rebuilder;
|
std::unique_ptr<RebuildThread> rebuilder;
|
||||||
|
|
||||||
|
// transient rowIDs (not actually present in the on-disk DB, only in our
|
||||||
|
// in-memory cache / temporary table) start at this value and count down
|
||||||
|
PositionedID nextTransientId = -1000;
|
||||||
};
|
};
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
@ -1058,8 +1115,8 @@ FGPositioned* NavDataCache::NavDataCachePrivate::loadById(sqlite3_int64 rowid,
|
||||||
case FGPositioned::CITY:
|
case FGPositioned::CITY:
|
||||||
case FGPositioned::TOWN:
|
case FGPositioned::TOWN:
|
||||||
case FGPositioned::VILLAGE:
|
case FGPositioned::VILLAGE:
|
||||||
{
|
case FGPositioned::VISUAL_REPORTING_POINT: {
|
||||||
FGPositioned* wpt = new FGPositioned(rowid, ty, ident, pos);
|
FGPositioned* wpt = new POI(rowid, ty, ident, pos, name);
|
||||||
return wpt;
|
return wpt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2009,14 +2066,14 @@ void NavDataCache::clearDynamicPositioneds()
|
||||||
|
|
||||||
FGPositionedRef NavDataCache::loadById(PositionedID rowid)
|
FGPositionedRef NavDataCache::loadById(PositionedID rowid)
|
||||||
{
|
{
|
||||||
if (rowid < 1) {
|
if (!d || (rowid == 0)) {
|
||||||
return NULL;
|
return {};
|
||||||
}
|
};
|
||||||
if (!d) return NULL;
|
|
||||||
PositionedCache::iterator it = d->cache.find(rowid);
|
PositionedCache::iterator it = d->cache.find(rowid);
|
||||||
if (it != d->cache.end()) {
|
if (it != d->cache.end()) {
|
||||||
d->cacheHits++;
|
d->cacheHits++;
|
||||||
return it->second; // cache it
|
return it->second; // cache hit
|
||||||
}
|
}
|
||||||
|
|
||||||
sqlite3_int64 aptId;
|
sqlite3_int64 aptId;
|
||||||
|
@ -2060,35 +2117,62 @@ PositionedID NavDataCache::insertAirport(FGPositioned::Type ty, const string& id
|
||||||
|
|
||||||
void NavDataCache::updatePosition(PositionedID item, const SGGeod &pos)
|
void NavDataCache::updatePosition(PositionedID item, const SGGeod &pos)
|
||||||
{
|
{
|
||||||
if (d->cache.find(item) != d->cache.end()) {
|
const bool isTemporary = (item < 0);
|
||||||
SG_LOG(SG_NAVCACHE, SG_DEBUG, "updating position of an item in the cache");
|
SGVec3d cartPos(SGVec3d::fromGeod(pos));
|
||||||
|
auto it = d->cache.find(item);
|
||||||
|
|
||||||
|
// transient item, update the transient octree : this is much easier than the
|
||||||
|
// persistent octree case (see logic below) becuase we know the tree is fully
|
||||||
|
// loaded, and there's no DB table to keep in sync; we just update the in-memory
|
||||||
|
// leaves once we found them.
|
||||||
|
if (isTemporary) {
|
||||||
|
assert(it != d->cache.end()); // transient item must existing in the in-memory cache
|
||||||
|
|
||||||
|
SGVec3d oldCartPos(SGVec3d::fromGeod(it->second->geod()));
|
||||||
|
const auto ty = it->second->type();
|
||||||
|
|
||||||
|
Octree::Leaf* oldOctreeLeaf = Octree::globalTransientOctree()->findLeafForPos(oldCartPos);
|
||||||
|
Octree::Leaf* newOctreeLeaf = Octree::globalTransientOctree()->findLeafForPos(cartPos);
|
||||||
|
|
||||||
|
// commonly the octree leaf won't change, so check for that
|
||||||
|
if (oldOctreeLeaf != newOctreeLeaf) {
|
||||||
|
oldOctreeLeaf->removeChild(item);
|
||||||
|
newOctreeLeaf->insertChild(ty, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
it->second->modifyPosition(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (it != d->cache.end()) {
|
||||||
d->cache[item]->modifyPosition(pos);
|
d->cache[item]->modifyPosition(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
SGVec3d cartPos(SGVec3d::fromGeod(pos));
|
auto stmt = isTemporary ? d->updateTempPos : d->updatePosition;
|
||||||
|
sqlite3_bind_int(stmt, 1, item);
|
||||||
|
sqlite3_bind_double(stmt, 2, pos.getLongitudeDeg());
|
||||||
|
sqlite3_bind_double(stmt, 3, pos.getLatitudeDeg());
|
||||||
|
sqlite3_bind_double(stmt, 4, pos.getElevationM());
|
||||||
|
|
||||||
sqlite3_bind_int(d->setAirportPos, 1, item);
|
if (!isTemporary) {
|
||||||
sqlite3_bind_double(d->setAirportPos, 2, pos.getLongitudeDeg());
|
// bug 905; the octree leaf may change here, but the leaf may already be
|
||||||
sqlite3_bind_double(d->setAirportPos, 3, pos.getLatitudeDeg());
|
// loaded, and caching its children. (Either the old or new leaf!). Worse,
|
||||||
sqlite3_bind_double(d->setAirportPos, 4, pos.getElevationM());
|
// we may be called here as a result of loading one of those leaf's children.
|
||||||
|
// instead of dealing with all those possibilites, such as modifying
|
||||||
// bug 905; the octree leaf may change here, but the leaf may already be
|
// the in-memory leaf's STL child container, we simply leave the runtime
|
||||||
// loaded, and caching its children. (Either the old or new leaf!). Worse,
|
// structures alone. This is fine providing items do no move very far, since
|
||||||
// we may be called here as a result of loading one of those leaf's children.
|
// all the spatial searches ultimately use the items' real cartesian position,
|
||||||
// instead of dealing with all those possibilites, such as modifying
|
// which was updated above.
|
||||||
// the in-memory leaf's STL child container, we simply leave the runtime
|
|
||||||
// structures alone. This is fine providing items do no move very far, since
|
|
||||||
// all the spatial searches ultimately use the items' real cartesian position,
|
|
||||||
// which was updated above.
|
|
||||||
Octree::Leaf* octreeLeaf = Octree::globalPersistentOctree()->findLeafForPos(cartPos);
|
Octree::Leaf* octreeLeaf = Octree::globalPersistentOctree()->findLeafForPos(cartPos);
|
||||||
sqlite3_bind_int64(d->setAirportPos, 5, octreeLeaf->guid());
|
sqlite3_bind_int64(stmt, 5, octreeLeaf->guid());
|
||||||
|
}
|
||||||
|
|
||||||
sqlite3_bind_double(d->setAirportPos, 6, cartPos.x());
|
sqlite3_bind_double(stmt, 6, cartPos.x());
|
||||||
sqlite3_bind_double(d->setAirportPos, 7, cartPos.y());
|
sqlite3_bind_double(stmt, 7, cartPos.y());
|
||||||
sqlite3_bind_double(d->setAirportPos, 8, cartPos.z());
|
sqlite3_bind_double(stmt, 8, cartPos.z());
|
||||||
|
|
||||||
|
|
||||||
d->execUpdate(d->setAirportPos);
|
d->execUpdate(stmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavDataCache::insertTower(PositionedID airportId, const SGGeod& pos)
|
void NavDataCache::insertTower(PositionedID airportId, const SGGeod& pos)
|
||||||
|
@ -2215,21 +2299,32 @@ PositionedID NavDataCache::insertCommStation(FGPositioned::Type ty,
|
||||||
return d->execInsert(d->insertCommStation);
|
return d->execInsert(d->insertCommStation);
|
||||||
}
|
}
|
||||||
|
|
||||||
PositionedID NavDataCache::insertFix(const std::string& ident, const SGGeod& aPos)
|
PositionedID NavDataCache::createPOI(FGPositioned::Type ty, const std::string& ident, const SGGeod& aPos,
|
||||||
|
const std::string& name, bool isTransient)
|
||||||
{
|
{
|
||||||
return d->insertPositioned(FGPositioned::FIX, ident, string(), aPos, 0, true);
|
if (isTransient) {
|
||||||
}
|
return d->insertTemporaryPositioned(createTransientID(), ty, ident, name, aPos,
|
||||||
|
|
||||||
PositionedID NavDataCache::createPOI(FGPositioned::Type ty, const std::string& ident, const SGGeod& aPos)
|
|
||||||
{
|
|
||||||
return d->insertPositioned(ty, ident, string(), aPos, 0,
|
|
||||||
true /* spatial index */);
|
true /* spatial index */);
|
||||||
|
} else {
|
||||||
|
return d->insertPositioned(ty, ident, name, aPos, 0,
|
||||||
|
true /* spatial index */);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NavDataCache::removePOI(FGPositioned::Type ty, const std::string& aIdent)
|
bool NavDataCache::removePOI(FGPositionedRef ref)
|
||||||
{
|
{
|
||||||
d->removePositionedWithIdent(ty, aIdent);
|
const bool isTemporary = ref->guid() < 0;
|
||||||
// should remove from the live cache too?
|
// remove from the octree if temporary.
|
||||||
|
if (isTemporary) {
|
||||||
|
auto octreeLeaf = Octree::globalTransientOctree()->findLeafForPos(ref->cart());
|
||||||
|
octreeLeaf->removeChild(ref->guid());
|
||||||
|
}
|
||||||
|
|
||||||
|
d->removePositioned(ref->guid());
|
||||||
|
|
||||||
|
auto it = d->cache.find(ref->guid());
|
||||||
|
d->cache.erase(it);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -2811,6 +2906,11 @@ SGPath NavDataCache::path() const
|
||||||
return d->path;
|
return d->path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PositionedID NavDataCache::createTransientID()
|
||||||
|
{
|
||||||
|
return d->nextTransientId--;
|
||||||
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Transaction RAII object
|
// Transaction RAII object
|
||||||
|
|
||||||
|
|
|
@ -182,11 +182,10 @@ public:
|
||||||
PositionedID insertCommStation(FGPositioned::Type ty,
|
PositionedID insertCommStation(FGPositioned::Type ty,
|
||||||
const std::string& name, const SGGeod& pos, int freq, int range,
|
const std::string& name, const SGGeod& pos, int freq, int range,
|
||||||
PositionedID apt);
|
PositionedID apt);
|
||||||
PositionedID insertFix(const std::string& ident, const SGGeod& aPos);
|
|
||||||
|
|
||||||
PositionedID createPOI(FGPositioned::Type ty, const std::string& ident, const SGGeod& aPos);
|
PositionedID createPOI(FGPositioned::Type ty, const std::string& ident, const SGGeod& aPos, const std::string& aName, bool transient);
|
||||||
|
|
||||||
bool removePOI(FGPositioned::Type ty, const std::string& aIdent);
|
bool removePOI(FGPositionedRef wpt);
|
||||||
|
|
||||||
/// update the metar flag associated with an airport
|
/// update the metar flag associated with an airport
|
||||||
void setAirportMetar(const std::string& icao, bool hasMetar);
|
void setAirportMetar(const std::string& icao, bool hasMetar);
|
||||||
|
@ -336,6 +335,8 @@ public:
|
||||||
|
|
||||||
void clearDynamicPositioneds();
|
void clearDynamicPositioneds();
|
||||||
|
|
||||||
|
PositionedID createTransientID();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
NavDataCache();
|
NavDataCache();
|
||||||
|
|
||||||
|
|
|
@ -1,26 +1,9 @@
|
||||||
/**
|
/*
|
||||||
* PositionedOctree - define a spatial octree containing Positioned items
|
* SPDX-FileCopyrightText: (C) 2012 James Turner <james@flightgear.org>
|
||||||
* arranged by their global cartesian position.
|
* SPDX_FileComment: define a spatial octree containing Positioned items
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Written by James Turner, started 2012.
|
|
||||||
//
|
|
||||||
// Copyright (C) 2012 James Turner
|
|
||||||
//
|
|
||||||
// This program is free software; you can redistribute it and/or
|
|
||||||
// modify it under the terms of the GNU General Public License as
|
|
||||||
// published by the Free Software Foundation; either version 2 of the
|
|
||||||
// License, or (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful, but
|
|
||||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
// General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with this program; if not, write to the Free Software
|
|
||||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include "PositionedOctree.hxx"
|
#include "PositionedOctree.hxx"
|
||||||
|
@ -152,6 +135,20 @@ void Leaf::insertChild(FGPositioned::Type ty, PositionedID id)
|
||||||
children.insert(children.end(), TypedPositioned(ty, id));
|
children.insert(children.end(), TypedPositioned(ty, id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Leaf::removeChild(PositionedID id)
|
||||||
|
{
|
||||||
|
// only for use with transient items, i.e negative IDs
|
||||||
|
assert(id < 0);
|
||||||
|
|
||||||
|
auto it = std::find_if(children.begin(), children.end(), [id](const TypedPositioned& p) {
|
||||||
|
return p.second == id;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (it != children.end()) {
|
||||||
|
children.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Leaf::loadChildren()
|
void Leaf::loadChildren()
|
||||||
{
|
{
|
||||||
if (_childrenLoaded) {
|
if (_childrenLoaded) {
|
||||||
|
@ -337,6 +334,7 @@ bool findNearestN(const SGVec3d& aPos, unsigned int aN, double aCutoffM, FGPosit
|
||||||
FindNearestPQueue pq;
|
FindNearestPQueue pq;
|
||||||
FindNearestResults results;
|
FindNearestResults results;
|
||||||
pq.push(Ordered<Node*>(globalPersistentOctree(), 0));
|
pq.push(Ordered<Node*>(globalPersistentOctree(), 0));
|
||||||
|
pq.push(Ordered<Node*>(globalTransientOctree(), 0));
|
||||||
double cut = aCutoffM;
|
double cut = aCutoffM;
|
||||||
|
|
||||||
SGTimeStamp tm;
|
SGTimeStamp tm;
|
||||||
|
@ -378,6 +376,7 @@ bool findAllWithinRange(const SGVec3d& aPos, double aRangeM, FGPositioned::Filte
|
||||||
FindNearestPQueue pq;
|
FindNearestPQueue pq;
|
||||||
FindNearestResults results;
|
FindNearestResults results;
|
||||||
pq.push(Ordered<Node*>(globalPersistentOctree(), 0));
|
pq.push(Ordered<Node*>(globalPersistentOctree(), 0));
|
||||||
|
pq.push(Ordered<Node*>(globalTransientOctree(), 0));
|
||||||
double rng = aRangeM;
|
double rng = aRangeM;
|
||||||
|
|
||||||
SGTimeStamp tm;
|
SGTimeStamp tm;
|
||||||
|
|
|
@ -1,28 +1,10 @@
|
||||||
/**
|
/*
|
||||||
* PositionedOctree - define a spatial octree containing Positioned items
|
* SPDX-FileCopyrightText: (C) 2012 James Turner <james@flightgear.org>
|
||||||
* arranged by their global cartesian position.
|
* SPDX_FileComment: define a spatial octree containing Positioned items
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Written by James Turner, started 2012.
|
#pragma once
|
||||||
//
|
|
||||||
// Copyright (C) 2012 James Turner
|
|
||||||
//
|
|
||||||
// This program is free software; you can redistribute it and/or
|
|
||||||
// modify it under the terms of the GNU General Public License as
|
|
||||||
// published by the Free Software Foundation; either version 2 of the
|
|
||||||
// License, or (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful, but
|
|
||||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
// General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with this program; if not, write to the Free Software
|
|
||||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
||||||
|
|
||||||
#ifndef FG_POSITIONED_OCTREE_HXX
|
|
||||||
#define FG_POSITIONED_OCTREE_HXX
|
|
||||||
|
|
||||||
// std
|
// std
|
||||||
#include <array>
|
#include <array>
|
||||||
|
@ -193,8 +175,9 @@ namespace Octree
|
||||||
}
|
}
|
||||||
|
|
||||||
void insertChild(FGPositioned::Type ty, PositionedID id);
|
void insertChild(FGPositioned::Type ty, PositionedID id);
|
||||||
|
void removeChild(PositionedID id);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool _childrenLoaded = false;
|
bool _childrenLoaded = false;
|
||||||
|
|
||||||
typedef std::multimap<FGPositioned::Type, PositionedID> ChildMap;
|
typedef std::multimap<FGPositioned::Type, PositionedID> ChildMap;
|
||||||
|
@ -253,4 +236,3 @@ namespace Octree
|
||||||
|
|
||||||
} // of namespace flightgear
|
} // of namespace flightgear
|
||||||
|
|
||||||
#endif // of FG_POSITIONED_OCTREE_HXX
|
|
||||||
|
|
|
@ -422,12 +422,12 @@ void Airway::Network::addEdge(int aWay, const SGGeod& aStartPos,
|
||||||
|
|
||||||
if (!start) {
|
if (!start) {
|
||||||
SG_LOG(SG_NAVAID, SG_DEBUG, "unknown airways start pt: '" << aStartIdent << "'");
|
SG_LOG(SG_NAVAID, SG_DEBUG, "unknown airways start pt: '" << aStartIdent << "'");
|
||||||
start = FGPositioned::createUserWaypoint(aStartIdent, aStartPos);
|
start = FGPositioned::createWaypoint(FGPositioned::WAYPOINT, aStartIdent, aStartPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!end) {
|
if (!end) {
|
||||||
SG_LOG(SG_NAVAID, SG_DEBUG, "unknown airways end pt: '" << aEndIdent << "'");
|
SG_LOG(SG_NAVAID, SG_DEBUG, "unknown airways end pt: '" << aEndIdent << "'");
|
||||||
end = FGPositioned::createUserWaypoint(aEndIdent, aEndPos);
|
end = FGPositioned::createWaypoint(FGPositioned::WAYPOINT, aEndIdent, aEndPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
NavDataCache::instance()->insertEdge(_networkID, aWay, start->guid(), end->guid());
|
NavDataCache::instance()->insertEdge(_networkID, aWay, start->guid(), end->guid());
|
||||||
|
|
|
@ -145,7 +145,7 @@ void FixesLoader::loadFixes(const NavDataCache::SceneryLocation& sceneryLocation
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!duplicate) {
|
if (!duplicate) {
|
||||||
_cache->insertFix(ident, pos);
|
_cache->createPOI(FGPositioned::FIX, ident, pos, {}, false);
|
||||||
_loadedFixes.insert({ident, pos});
|
_loadedFixes.insert({ident, pos});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,26 +1,10 @@
|
||||||
// poidb.cxx -- points of interest management routines
|
/*
|
||||||
//
|
* SPDX-FileCopyrightText: (C) Christian Schmitt, March 2013
|
||||||
// Written by Christian Schmitt, March 2013
|
* SPDX_FileComment: points of interest management routines
|
||||||
//
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
// This program is free software; you can redistribute it and/or
|
*/
|
||||||
// modify it under the terms of the GNU General Public License as
|
|
||||||
// published by the Free Software Foundation; either version 2 of the
|
|
||||||
// License, or (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful, but
|
|
||||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
// General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with this program; if not, write to the Free Software
|
|
||||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
#include "config.h"
|
||||||
# include "config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <istream> // std::ws
|
#include <istream> // std::ws
|
||||||
#include "poidb.hxx"
|
#include "poidb.hxx"
|
||||||
|
@ -45,6 +29,10 @@ mapPOITypeToFGPType(int aTy)
|
||||||
case 12: return FGPositioned::CITY;
|
case 12: return FGPositioned::CITY;
|
||||||
case 13: return FGPositioned::TOWN;
|
case 13: return FGPositioned::TOWN;
|
||||||
case 14: return FGPositioned::VILLAGE;
|
case 14: return FGPositioned::VILLAGE;
|
||||||
|
|
||||||
|
case 1000: return FGPositioned::VISUAL_REPORTING_POINT;
|
||||||
|
case 1001: return FGPositioned::WAYPOINT;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw sg_range_exception("Unknown POI type", "FGNavDataCache::readPOIFromStream");
|
throw sg_range_exception("Unknown POI type", "FGNavDataCache::readPOIFromStream");
|
||||||
}
|
}
|
||||||
|
@ -87,7 +75,7 @@ static PositionedID readPOIFromStream(std::istream& aStream, NavDataCache* cache
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return cache->createPOI(type, name, pos);
|
return cache->createPOI(type, name, pos, name, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// load and initialize the POI database
|
// load and initialize the POI database
|
||||||
|
|
|
@ -1,26 +1,10 @@
|
||||||
// poidb.cxx -- points of interest management routines
|
/*
|
||||||
//
|
* SPDX-FileCopyrightText: (C) Christian Schmitt, March 2013
|
||||||
// Written by Christian Schmitt, March 2013
|
* SPDX_FileComment: points of interest management routines
|
||||||
//
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
// This program is free software; you can redistribute it and/or
|
*/
|
||||||
// modify it under the terms of the GNU General Public License as
|
|
||||||
// published by the Free Software Foundation; either version 2 of the
|
|
||||||
// License, or (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful, but
|
|
||||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
// General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with this program; if not, write to the Free Software
|
|
||||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
#ifndef _FG_POIDB_HXX
|
|
||||||
#define _FG_POIDB_HXX
|
|
||||||
|
|
||||||
|
|
||||||
#include <simgear/compiler.h>
|
#include <simgear/compiler.h>
|
||||||
|
@ -37,4 +21,3 @@ bool poiDBInit(const SGPath& path);
|
||||||
|
|
||||||
} // of namespace flightgear
|
} // of namespace flightgear
|
||||||
|
|
||||||
#endif // _FG_NAVDB_HXX
|
|
||||||
|
|
|
@ -1,22 +1,8 @@
|
||||||
// positioned.cxx - base class for objects which are positioned
|
/*
|
||||||
//
|
* SPDX-FileCopyrightText: (C) 2008 James Turner <james@flightgear.org>
|
||||||
// Copyright (C) 2008 James Turner
|
* SPDX_FileComment: base class for objects which are spatially located in the simulated world
|
||||||
//
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
// This program is free software; you can redistribute it and/or
|
*/
|
||||||
// modify it under the terms of the GNU General Public License as
|
|
||||||
// published by the Free Software Foundation; either version 2 of the
|
|
||||||
// License, or (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful, but
|
|
||||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
// General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with this program; if not, write to the Free Software
|
|
||||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
@ -60,8 +46,6 @@ static bool validateFilter(FGPositioned::Filter* filter)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const PositionedID FGPositioned::TRANSIENT_ID = -2;
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
FGPositioned::FGPositioned( PositionedID aGuid,
|
FGPositioned::FGPositioned( PositionedID aGuid,
|
||||||
|
@ -118,25 +102,55 @@ bool FGPositioned::isNavaidType(FGPositioned* pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool isValidCustomWaypointType(FGPositioned::Type ty)
|
||||||
|
{
|
||||||
|
switch (ty) {
|
||||||
|
case FGPositioned::WAYPOINT:
|
||||||
|
case FGPositioned::FIX:
|
||||||
|
case FGPositioned::VISUAL_REPORTING_POINT:
|
||||||
|
case FGPositioned::OBSTACLE:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
FGPositionedRef
|
FGPositionedRef
|
||||||
FGPositioned::createUserWaypoint(const std::string& aIdent, const SGGeod& aPos)
|
FGPositioned::createWaypoint(FGPositioned::Type aType, const std::string& aIdent, const SGGeod& aPos,
|
||||||
|
bool isTemporary,
|
||||||
|
const std::string& aName)
|
||||||
{
|
{
|
||||||
NavDataCache* cache = NavDataCache::instance();
|
NavDataCache* cache = NavDataCache::instance();
|
||||||
TypeFilter filter(WAYPOINT);
|
|
||||||
FGPositionedList existing = cache->findAllWithIdent(aIdent, &filter, true);
|
if (!isValidCustomWaypointType(aType)) {
|
||||||
if (!existing.empty()) {
|
throw std::logic_error(std::string{"Create waypoint: not allowed for type:"} + nameForType(aType));
|
||||||
SG_LOG(SG_NAVAID, SG_WARN, "attempt to insert duplicate WAYPOINT:" << aIdent);
|
|
||||||
return existing.front();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PositionedID id = cache->createPOI(WAYPOINT, aIdent, aPos);
|
TypeFilter filter(aType);
|
||||||
|
|
||||||
|
FGPositionedRef existing = cache->findClosestWithIdent(aIdent, aPos, &filter);
|
||||||
|
if (existing) {
|
||||||
|
auto distanceNm = SGGeodesy::distanceNm(existing->geod(), aPos);
|
||||||
|
if (distanceNm < 100) {
|
||||||
|
SG_LOG(SG_NAVAID, SG_WARN, "attempt to insert duplicate waypoint:" << aIdent << " within 100nm of existing waypoint with same ident");
|
||||||
|
return existing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const PositionedID id = cache->createPOI(aType, aIdent, aPos, aName, isTemporary);
|
||||||
return cache->loadById(id);
|
return cache->loadById(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FGPositioned::deleteUserWaypoint(const std::string& aIdent)
|
bool FGPositioned::deleteWaypoint(FGPositionedRef ref)
|
||||||
{
|
{
|
||||||
NavDataCache* cache = NavDataCache::instance();
|
NavDataCache* cache = NavDataCache::instance();
|
||||||
return cache->removePOI(WAYPOINT, aIdent);
|
const auto ty = ref->type();
|
||||||
|
if (!POI::isType(ty) && (ty != FGPositioned::FIX)) {
|
||||||
|
SG_LOG(SG_NAVAID, SG_DEV_WARN, "attempt to remove non-POI waypoint:" << ref->ident());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cache->removePOI(ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -192,7 +206,8 @@ FGPositioned::Type FGPositioned::typeFromName(const std::string& aName)
|
||||||
{"mobile-tacan", MOBILE_TACAN},
|
{"mobile-tacan", MOBILE_TACAN},
|
||||||
{"obstacle", OBSTACLE},
|
{"obstacle", OBSTACLE},
|
||||||
{"parking", PARKING},
|
{"parking", PARKING},
|
||||||
{"taxi-node",TAXI_NODE},
|
{"taxi-node", TAXI_NODE},
|
||||||
|
{"visual-reporting-point", VISUAL_REPORTING_POINT},
|
||||||
|
|
||||||
// aliases
|
// aliases
|
||||||
{"localizer", LOC},
|
{"localizer", LOC},
|
||||||
|
@ -208,9 +223,9 @@ FGPositioned::Type FGPositioned::typeFromName(const std::string& aName)
|
||||||
{"middle-marker", MM},
|
{"middle-marker", MM},
|
||||||
{"inner-marker", IM},
|
{"inner-marker", IM},
|
||||||
{"parking-stand", PARKING},
|
{"parking-stand", PARKING},
|
||||||
|
{"vrp", VISUAL_REPORTING_POINT},
|
||||||
|
|
||||||
{NULL, INVALID}
|
{NULL, INVALID}};
|
||||||
};
|
|
||||||
|
|
||||||
std::string lowerName = simgear::strutils::lowercase(aName);
|
std::string lowerName = simgear::strutils::lowercase(aName);
|
||||||
|
|
||||||
|
@ -259,6 +274,9 @@ const char* FGPositioned::nameForType(Type aTy)
|
||||||
case CITY: return "city";
|
case CITY: return "city";
|
||||||
case TOWN: return "town";
|
case TOWN: return "town";
|
||||||
case VILLAGE: return "village";
|
case VILLAGE: return "village";
|
||||||
|
case VISUAL_REPORTING_POINT: return "visual-reporting-point";
|
||||||
|
case MOBILE_TACAN: return "mobile-tacan";
|
||||||
|
case OBSTACLE: return "obstacle";
|
||||||
default:
|
default:
|
||||||
return "unknown";
|
return "unknown";
|
||||||
}
|
}
|
||||||
|
@ -493,3 +511,13 @@ FGPositioned::TypeFilter::pass(FGPositioned* aPos) const
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
POI::POI(PositionedID aGuid, Type ty, const std::string& aIdent, const SGGeod& aPos, const std::string& aName) : FGPositioned(aGuid, ty, aIdent, aPos),
|
||||||
|
mName(aName)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool POI::isType(FGPositioned::Type ty)
|
||||||
|
{
|
||||||
|
return (ty == FGPositioned::WAYPOINT) || (ty == FGPositioned::OBSTACLE) ||
|
||||||
|
((ty >= FGPositioned::COUNTRY) && (ty < FGPositioned::LAST_POI_TYPE));
|
||||||
|
}
|
||||||
|
|
|
@ -1,25 +1,10 @@
|
||||||
// positioned.hxx - base class for objects which are positioned
|
/*
|
||||||
//
|
* SPDX-FileCopyrightText: (C) 2008 James Turner <james@flightgear.org>
|
||||||
// Copyright (C) 2008 James Turner
|
* SPDX_FileComment: base class for objects which are spatially located in the simulated world
|
||||||
//
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
// This program is free software; you can redistribute it and/or
|
*/
|
||||||
// modify it under the terms of the GNU General Public License as
|
|
||||||
// published by the Free Software Foundation; either version 2 of the
|
|
||||||
// License, or (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful, but
|
|
||||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
// General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with this program; if not, write to the Free Software
|
|
||||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
||||||
//
|
|
||||||
// $Id$
|
|
||||||
|
|
||||||
#ifndef FG_POSITIONED_HXX
|
#pragma once
|
||||||
#define FG_POSITIONED_HXX
|
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
@ -42,8 +27,6 @@ namespace flightgear { class NavDataCache; }
|
||||||
class FGPositioned : public SGReferenced
|
class FGPositioned : public SGReferenced
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static const PositionedID TRANSIENT_ID;
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
INVALID = 0,
|
INVALID = 0,
|
||||||
AIRPORT,
|
AIRPORT,
|
||||||
|
@ -91,7 +74,8 @@ public:
|
||||||
CITY,
|
CITY,
|
||||||
TOWN,
|
TOWN,
|
||||||
VILLAGE,
|
VILLAGE,
|
||||||
|
VISUAL_REPORTING_POINT,
|
||||||
|
LAST_POI_TYPE,
|
||||||
LAST_TYPE
|
LAST_TYPE
|
||||||
} Type;
|
} Type;
|
||||||
|
|
||||||
|
@ -295,13 +279,16 @@ public:
|
||||||
*/
|
*/
|
||||||
static const char* nameForType(Type aTy);
|
static const char* nameForType(Type aTy);
|
||||||
|
|
||||||
static FGPositionedRef createUserWaypoint(const std::string& aIdent, const SGGeod& aPos);
|
static FGPositionedRef createWaypoint(FGPositioned::Type aType, const std::string& aIdent, const SGGeod& aPos,
|
||||||
static bool deleteUserWaypoint(const std::string& aIdent);
|
bool isTemporary = false,
|
||||||
|
const std::string& aName = {});
|
||||||
|
static bool deleteWaypoint(FGPositionedRef aWpt);
|
||||||
protected:
|
protected:
|
||||||
friend class flightgear::NavDataCache;
|
friend class flightgear::NavDataCache;
|
||||||
|
|
||||||
FGPositioned(PositionedID aGuid, Type ty, const std::string& aIdent, const SGGeod& aPos);
|
FGPositioned(PositionedID aGuid, Type ty, const std::string& aIdent, const SGGeod& aPos);
|
||||||
|
|
||||||
|
// called by NavDataCache::updatePosition
|
||||||
void modifyPosition(const SGGeod& newPos);
|
void modifyPosition(const SGGeod& newPos);
|
||||||
void invalidatePosition();
|
void invalidatePosition();
|
||||||
|
|
||||||
|
@ -318,6 +305,8 @@ private:
|
||||||
const SGVec3d mCart;
|
const SGVec3d mCart;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
T* fgpositioned_cast(FGPositioned* p)
|
T* fgpositioned_cast(FGPositioned* p)
|
||||||
{
|
{
|
||||||
|
@ -340,4 +329,18 @@ T* fgpositioned_cast(FGPositionedRef p)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // of FG_POSITIONED_HXX
|
class POI : public FGPositioned
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
POI(PositionedID aGuid, Type ty, const std::string& aIdent, const SGGeod& aPos, const std::string& aName);
|
||||||
|
|
||||||
|
const std::string& name() const override
|
||||||
|
{
|
||||||
|
return mName;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isType(FGPositioned::Type ty);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string mName;
|
||||||
|
};
|
|
@ -72,6 +72,9 @@ static naGhostType FixGhostType = { positionedGhostDestroy, "fix", fixGhostGetMe
|
||||||
static const char* commGhostGetMember(naContext c, void* g, naRef field, naRef* out);
|
static const char* commGhostGetMember(naContext c, void* g, naRef field, naRef* out);
|
||||||
static naGhostType CommGhostType = {positionedGhostDestroy, "comm", commGhostGetMember, nullptr};
|
static naGhostType CommGhostType = {positionedGhostDestroy, "comm", commGhostGetMember, nullptr};
|
||||||
|
|
||||||
|
static const char* poiGhostGetMember(naContext c, void* g, naRef field, naRef* out);
|
||||||
|
static naGhostType POIGhostType = {positionedGhostDestroy, "poi", poiGhostGetMember, nullptr};
|
||||||
|
|
||||||
static void hashset(naContext c, naRef hash, const char* key, naRef val)
|
static void hashset(naContext c, naRef hash, const char* key, naRef val)
|
||||||
{
|
{
|
||||||
naRef s = naNewString(c);
|
naRef s = naNewString(c);
|
||||||
|
@ -92,7 +95,8 @@ FGPositioned* positionedGhost(naRef r)
|
||||||
(naGhost_type(r) == &NavaidGhostType) ||
|
(naGhost_type(r) == &NavaidGhostType) ||
|
||||||
(naGhost_type(r) == &RunwayGhostType) ||
|
(naGhost_type(r) == &RunwayGhostType) ||
|
||||||
(naGhost_type(r) == &FixGhostType) ||
|
(naGhost_type(r) == &FixGhostType) ||
|
||||||
(naGhost_type(r) == &CommGhostType)) {
|
(naGhost_type(r) == &FixGhostType) ||
|
||||||
|
(naGhost_type(r) == &POIGhostType)) {
|
||||||
return (FGPositioned*) naGhost_ptr(r);
|
return (FGPositioned*) naGhost_ptr(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,12 +138,22 @@ static FGFix* fixGhost(naRef r)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
static flightgear::CommStation* commGhost(naRef r)
|
static flightgear::CommStation* commGhost(naRef r)
|
||||||
{
|
{
|
||||||
if (naGhost_type(r) == &CommGhostType)
|
if (naGhost_type(r) == &CommGhostType)
|
||||||
return (flightgear::CommStation*)naGhost_ptr(r);
|
return (flightgear::CommStation*)naGhost_ptr(r);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static POI* poiGhost(naRef r)
|
||||||
|
{
|
||||||
|
if (naGhost_type(r) == &POIGhostType)
|
||||||
|
return (POI*)naGhost_ptr(r);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void positionedGhostDestroy(void* g)
|
static void positionedGhostDestroy(void* g)
|
||||||
{
|
{
|
||||||
|
@ -151,6 +165,7 @@ static void positionedGhostDestroy(void* g)
|
||||||
|
|
||||||
static naRef airportPrototype;
|
static naRef airportPrototype;
|
||||||
static naRef geoCoordClass;
|
static naRef geoCoordClass;
|
||||||
|
static naRef waypointPrototype;
|
||||||
|
|
||||||
naRef ghostForAirport(naContext c, const FGAirport* apt)
|
naRef ghostForAirport(naContext c, const FGAirport* apt)
|
||||||
{
|
{
|
||||||
|
@ -222,6 +237,16 @@ naRef ghostForComm(naContext c, const flightgear::CommStation* comm)
|
||||||
return naNewGhost2(c, &CommGhostType, (void*)comm);
|
return naNewGhost2(c, &CommGhostType, (void*)comm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
naRef ghostForPOI(naContext c, const POI* r)
|
||||||
|
{
|
||||||
|
if (!r) {
|
||||||
|
return naNil();
|
||||||
|
}
|
||||||
|
|
||||||
|
FGPositioned::get(r); // take a ref
|
||||||
|
return naNewGhost2(c, &POIGhostType, (void*)r);
|
||||||
|
}
|
||||||
|
|
||||||
naRef ghostForPositioned(naContext c, FGPositionedRef pos)
|
naRef ghostForPositioned(naContext c, FGPositionedRef pos)
|
||||||
{
|
{
|
||||||
if (!pos) {
|
if (!pos) {
|
||||||
|
@ -251,6 +276,14 @@ naRef ghostForPositioned(naContext c, FGPositionedRef pos)
|
||||||
case FGPositioned::FREQ_ENROUTE:
|
case FGPositioned::FREQ_ENROUTE:
|
||||||
return ghostForComm(c, fgpositioned_cast<flightgear::CommStation>(pos));
|
return ghostForComm(c, fgpositioned_cast<flightgear::CommStation>(pos));
|
||||||
|
|
||||||
|
case FGPositioned::WAYPOINT:
|
||||||
|
case FGPositioned::CITY:
|
||||||
|
case FGPositioned::COUNTRY:
|
||||||
|
case FGPositioned::TOWN:
|
||||||
|
case FGPositioned::VISUAL_REPORTING_POINT:
|
||||||
|
case FGPositioned::VILLAGE:
|
||||||
|
return ghostForPOI(c, fgpositioned_cast<POI>(pos));
|
||||||
|
|
||||||
default:
|
default:
|
||||||
SG_LOG(SG_NASAL, SG_DEV_ALERT, "Type lacks Nasal ghost mapping:" << pos->typeString());
|
SG_LOG(SG_NASAL, SG_DEV_ALERT, "Type lacks Nasal ghost mapping:" << pos->typeString());
|
||||||
return naNil();
|
return naNil();
|
||||||
|
@ -409,6 +442,8 @@ static const char* fixGhostGetMember(naContext c, void* g, naRef field, naRef* o
|
||||||
// for homogenity with other values returned by navinfo()
|
// for homogenity with other values returned by navinfo()
|
||||||
else if (!strcmp(fieldName, "type")) *out = stringToNasal(c, "fix");
|
else if (!strcmp(fieldName, "type")) *out = stringToNasal(c, "fix");
|
||||||
else if (!strcmp(fieldName, "name")) *out = stringToNasal(c, fix->ident());
|
else if (!strcmp(fieldName, "name")) *out = stringToNasal(c, fix->ident());
|
||||||
|
else if (!strcmp(fieldName, "guid"))
|
||||||
|
*out = naNum(fix->guid());
|
||||||
else {
|
else {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -433,6 +468,8 @@ static const char* commGhostGetMember(naContext c, void* g, naRef field, naRef*
|
||||||
*out = stringToNasal(c, comm->name());
|
*out = stringToNasal(c, comm->name());
|
||||||
else if (!strcmp(fieldName, "frequency")) {
|
else if (!strcmp(fieldName, "frequency")) {
|
||||||
*out = naNum(comm->freqMHz());
|
*out = naNum(comm->freqMHz());
|
||||||
|
} else if (!strcmp(fieldName, "guid")) {
|
||||||
|
*out = naNum(comm->guid());
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -440,6 +477,33 @@ static const char* commGhostGetMember(naContext c, void* g, naRef field, naRef*
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char* poiGhostGetMember(naContext c, void* g, naRef field, naRef* out)
|
||||||
|
{
|
||||||
|
const char* fieldName = naStr_data(field);
|
||||||
|
POI* wpt = (POI*)g;
|
||||||
|
|
||||||
|
if (!strcmp(fieldName, "parents")) {
|
||||||
|
*out = naNewVector(c);
|
||||||
|
naVec_append(*out, waypointPrototype);
|
||||||
|
} else if (!strcmp(fieldName, "id"))
|
||||||
|
*out = stringToNasal(c, wpt->ident());
|
||||||
|
else if (!strcmp(fieldName, "lat"))
|
||||||
|
*out = naNum(wpt->latitude());
|
||||||
|
else if (!strcmp(fieldName, "lon"))
|
||||||
|
*out = naNum(wpt->longitude());
|
||||||
|
else if (!strcmp(fieldName, "type"))
|
||||||
|
*out = stringToNasal(c, wpt->nameForType(wpt->type()));
|
||||||
|
else if (!strcmp(fieldName, "name"))
|
||||||
|
*out = stringToNasal(c, wpt->name());
|
||||||
|
else if (!strcmp(fieldName, "guid"))
|
||||||
|
*out = naNum(wpt->guid());
|
||||||
|
else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool hashIsCoord(naRef h)
|
static bool hashIsCoord(naRef h)
|
||||||
{
|
{
|
||||||
|
@ -540,6 +604,11 @@ int geodFromArgs(naRef* args, int offset, int argc, SGGeod& result)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (gt == &POIGhostType) {
|
||||||
|
result = poiGhost(args[offset])->geod();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
auto wp = wayptGhost(args[offset]);
|
auto wp = wayptGhost(args[offset]);
|
||||||
if (wp) {
|
if (wp) {
|
||||||
result = wp->position();
|
result = wp->position();
|
||||||
|
@ -1648,6 +1717,79 @@ static naRef f_tileIndex(naContext c, naRef me, int argc, naRef* args)
|
||||||
return naNum(b.gen_index());
|
return naNum(b.gen_index());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static naRef f_createWaypoint(naContext c, naRef me, int argc, naRef* args)
|
||||||
|
{
|
||||||
|
if ((argc < 3) || !naIsString(args[0]) || !naIsString(args[1])) {
|
||||||
|
naRuntimeError(c, "createWaypoint: expects type and ident as first two args");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string typeSpec(naStr_data(args[0]));
|
||||||
|
std::string ident(naStr_data(args[1]));
|
||||||
|
std::string name;
|
||||||
|
|
||||||
|
FGPositioned::Type wptType = FGPositioned::typeFromName(typeSpec);
|
||||||
|
|
||||||
|
SGGeod pos;
|
||||||
|
int argOffset = geodFromArgs(args, 2, argc, pos) + 2;
|
||||||
|
if ((argc > argOffset) && naIsString(args[argOffset])) {
|
||||||
|
name = naStr_data(args[argOffset]);
|
||||||
|
++argOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isTemporary = false;
|
||||||
|
if ((argc > argOffset) && naIsNum(args[argOffset])) {
|
||||||
|
isTemporary = (args[argOffset].num > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
auto wpt = FGPositioned::createWaypoint(wptType, ident, pos, isTemporary, name);
|
||||||
|
if (wptType == FGPositioned::FIX) {
|
||||||
|
return ghostForFix(c, fgpositioned_cast<FGFix>(wpt));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto poi = fgpositioned_cast<POI>(wpt);
|
||||||
|
return ghostForPOI(c, poi);
|
||||||
|
} catch (std::exception& e) {
|
||||||
|
naRuntimeError(c, "Failed to create waypoint: %s", e.what());
|
||||||
|
}
|
||||||
|
|
||||||
|
return naNil();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static naRef f_deleteWaypoint(naContext c, naRef me, int argc, naRef* args)
|
||||||
|
{
|
||||||
|
if (argc < 1) {
|
||||||
|
naRuntimeError(c, "deleteWaypoint: missing argument");
|
||||||
|
}
|
||||||
|
|
||||||
|
FGPositioned* wpt = poiGhost(args[0]);
|
||||||
|
if (!wpt && naIsNum(args[0])) {
|
||||||
|
PositionedID guid = static_cast<PositionedID>(args[0].num);
|
||||||
|
wpt = NavDataCache::instance()->loadById(guid);
|
||||||
|
if (!POI::isType(wpt->type())) {
|
||||||
|
naRuntimeError(c, "deleteWaypoint: only waypoints can be deleted for now");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FGPositioned::deleteWaypoint(wpt);
|
||||||
|
// note wpt is dead here, don't access
|
||||||
|
return naNil();
|
||||||
|
}
|
||||||
|
|
||||||
|
static naRef f_waypoint_move(naContext c, naRef me, int argc, naRef* args)
|
||||||
|
{
|
||||||
|
POI* wpt = poiGhost(me);
|
||||||
|
if (!wpt) {
|
||||||
|
naRuntimeError(c, "waypoint.move called on non-POI object");
|
||||||
|
}
|
||||||
|
|
||||||
|
SGGeod pos;
|
||||||
|
geodFromArgs(args, 0, argc, pos);
|
||||||
|
|
||||||
|
NavDataCache::instance()->updatePosition(wpt->guid(), pos);
|
||||||
|
return naNil();
|
||||||
|
}
|
||||||
|
|
||||||
void shutdownNasalPositioned()
|
void shutdownNasalPositioned()
|
||||||
{
|
{
|
||||||
|
@ -1711,6 +1853,8 @@ static struct {
|
||||||
{"greatCircleMove", f_greatCircleMove},
|
{"greatCircleMove", f_greatCircleMove},
|
||||||
{"tileIndex", f_tileIndex},
|
{"tileIndex", f_tileIndex},
|
||||||
{"tilePath", f_tilePath},
|
{"tilePath", f_tilePath},
|
||||||
|
{"createWaypoint", f_createWaypoint},
|
||||||
|
{"deleteWaypoint", f_deleteWaypoint},
|
||||||
{0, 0}};
|
{0, 0}};
|
||||||
|
|
||||||
|
|
||||||
|
@ -1740,6 +1884,10 @@ naRef initNasalPositioned(naRef globals, naContext c)
|
||||||
hashset(c, airportPrototype, "findBestRunwayForPos", naNewFunc(c, naNewCCode(c, f_airport_findBestRunway)));
|
hashset(c, airportPrototype, "findBestRunwayForPos", naNewFunc(c, naNewCCode(c, f_airport_findBestRunway)));
|
||||||
hashset(c, airportPrototype, "tostring", naNewFunc(c, naNewCCode(c, f_airport_toString)));
|
hashset(c, airportPrototype, "tostring", naNewFunc(c, naNewCCode(c, f_airport_toString)));
|
||||||
|
|
||||||
|
waypointPrototype = naNewHash(c);
|
||||||
|
naSave(c, waypointPrototype);
|
||||||
|
hashset(c, waypointPrototype, "move", naNewFunc(c, naNewCCode(c, f_waypoint_move)));
|
||||||
|
|
||||||
for(int i=0; funcs[i].name; i++) {
|
for(int i=0; funcs[i].name; i++) {
|
||||||
hashset(c, globals, funcs[i].name,
|
hashset(c, globals, funcs[i].name,
|
||||||
naNewFunc(c, naNewCCode(c, funcs[i].func)));
|
naNewFunc(c, naNewCCode(c, funcs[i].func)));
|
||||||
|
|
|
@ -18,8 +18,7 @@
|
||||||
// along with this program; if not, write to the Free Software
|
// along with this program; if not, write to the Free Software
|
||||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
#ifndef SCRIPTING_NASAL_POSITIONED_HXX
|
#pragma once
|
||||||
#define SCRIPTING_NASAL_POSITIONED_HXX
|
|
||||||
|
|
||||||
#include <simgear/nasal/nasal.h>
|
#include <simgear/nasal/nasal.h>
|
||||||
|
|
||||||
|
@ -48,5 +47,3 @@ naRef initNasalPositioned(naRef globals, naContext c);
|
||||||
naRef initNasalPositioned_cppbind(naRef globals, naContext c);
|
naRef initNasalPositioned_cppbind(naRef globals, naContext c);
|
||||||
void postinitNasalPositioned(naRef globals, naContext c);
|
void postinitNasalPositioned(naRef globals, naContext c);
|
||||||
void shutdownNasalPositioned();
|
void shutdownNasalPositioned();
|
||||||
|
|
||||||
#endif // of SCRIPTING_NASAL_POSITIONED_HXX
|
|
||||||
|
|
|
@ -459,6 +459,12 @@ SGPropertyNode_ptr propsFromString(const std::string& s)
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool geodsApproximatelyEqual(const SGGeod& a, const SGGeod& b)
|
||||||
|
{
|
||||||
|
return SGGeodesy::distanceM(a, b) < 50.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
namespace tearDown {
|
namespace tearDown {
|
||||||
|
|
||||||
void shutdownTestGlobals()
|
void shutdownTestGlobals()
|
||||||
|
|
|
@ -71,6 +71,8 @@ void writePointToKML(const std::string& ident, const SGGeod& pos);
|
||||||
|
|
||||||
bool executeNasal(const std::string& code);
|
bool executeNasal(const std::string& code);
|
||||||
|
|
||||||
|
bool geodsApproximatelyEqual(const SGGeod& a, const SGGeod& b);
|
||||||
|
|
||||||
namespace tearDown {
|
namespace tearDown {
|
||||||
|
|
||||||
void shutdownTestGlobals();
|
void shutdownTestGlobals();
|
||||||
|
|
|
@ -501,6 +501,8 @@ void RNAVProcedureTests::testEGPH_TLA6C()
|
||||||
|
|
||||||
CPPUNIT_ASSERT_EQUAL(std::string{"TLA"}, fp->legAtIndex(5)->waypoint()->ident());
|
CPPUNIT_ASSERT_EQUAL(std::string{"TLA"}, fp->legAtIndex(5)->waypoint()->ident());
|
||||||
CPPUNIT_ASSERT_EQUAL(std::string{"TLA"}, std::string{m_gpsNode->getStringValue("wp/wp[1]/ID")});
|
CPPUNIT_ASSERT_EQUAL(std::string{"TLA"}, std::string{m_gpsNode->getStringValue("wp/wp[1]/ID")});
|
||||||
|
|
||||||
|
SG_UNUSED(elapsed);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RNAVProcedureTests::testLFKC_AJO1R()
|
void RNAVProcedureTests::testLFKC_AJO1R()
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
#include "test_suite/FGTestApi/testGlobals.hxx"
|
#include "test_suite/FGTestApi/testGlobals.hxx"
|
||||||
#include "test_suite/FGTestApi/NavDataCache.hxx"
|
#include "test_suite/FGTestApi/NavDataCache.hxx"
|
||||||
|
|
||||||
|
#include <Airports/airport.hxx>
|
||||||
|
|
||||||
#include <Navaids/NavDataCache.hxx>
|
#include <Navaids/NavDataCache.hxx>
|
||||||
#include <Navaids/navrecord.hxx>
|
#include <Navaids/navrecord.hxx>
|
||||||
#include <Navaids/navlist.hxx>
|
#include <Navaids/navlist.hxx>
|
||||||
|
@ -34,3 +36,113 @@ void NavaidsTests::testBasic()
|
||||||
CPPUNIT_ASSERT_EQUAL(tla->get_freq(), 11570);
|
CPPUNIT_ASSERT_EQUAL(tla->get_freq(), 11570);
|
||||||
CPPUNIT_ASSERT_EQUAL(tla->get_range(), 130);
|
CPPUNIT_ASSERT_EQUAL(tla->get_range(), 130);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NavaidsTests::testCustomWaypoint()
|
||||||
|
{
|
||||||
|
// create a transaction, which we don't commit, to avoid making permanent DB changes
|
||||||
|
flightgear::NavDataCache::Transaction txn(flightgear::NavDataCache::instance());
|
||||||
|
|
||||||
|
SGGeod egccPos = SGGeod::fromDeg(-2.27, 53.35);
|
||||||
|
SGGeod offsetPos = SGGeodesy::direct(egccPos, 45.0, 20.0 * SG_NM_TO_METER);
|
||||||
|
auto poi = FGPositioned::createWaypoint(FGPositioned::WAYPOINT,
|
||||||
|
"TEST_WP0", offsetPos, false, "Lovely Waypoint");
|
||||||
|
CPPUNIT_ASSERT(poi);
|
||||||
|
CPPUNIT_ASSERT_EQUAL(poi->ident(), std::string{"TEST_WP0"});
|
||||||
|
CPPUNIT_ASSERT_EQUAL(poi->name(), std::string{"Lovely Waypoint"});
|
||||||
|
CPPUNIT_ASSERT(poi->guid() > 0);
|
||||||
|
CPPUNIT_ASSERT_EQUAL(poi->type(), FGPositioned::WAYPOINT);
|
||||||
|
CPPUNIT_ASSERT(FGTestApi::geodsApproximatelyEqual(offsetPos, poi->geod()));
|
||||||
|
|
||||||
|
// same but a FIX
|
||||||
|
SGGeod offsetPos2 = SGGeodesy::direct(egccPos, 180.0, 35.0 * SG_NM_TO_METER);
|
||||||
|
auto fix = FGPositioned::createWaypoint(FGPositioned::FIX,
|
||||||
|
"TEST_WP1", offsetPos2);
|
||||||
|
CPPUNIT_ASSERT_EQUAL(fix->ident(), std::string{"TEST_WP1"});
|
||||||
|
CPPUNIT_ASSERT_EQUAL(fix->name(), fix->ident());
|
||||||
|
CPPUNIT_ASSERT(fix->guid() > 0);
|
||||||
|
CPPUNIT_ASSERT_EQUAL(fix->type(), FGPositioned::FIX);
|
||||||
|
CPPUNIT_ASSERT(FGTestApi::geodsApproximatelyEqual(offsetPos2, fix->geod()));
|
||||||
|
|
||||||
|
// create same ident but far away
|
||||||
|
FGAirportRef vhhh = FGAirport::getByIdent("VHHH");
|
||||||
|
|
||||||
|
SGGeod pos3 = SGGeodesy::direct(vhhh->geod(), 10.0, 10.0 * SG_NM_TO_METER);
|
||||||
|
auto poi3 = FGPositioned::createWaypoint(FGPositioned::WAYPOINT,
|
||||||
|
"TEST_WP0", pos3, false, "Lovely Hong Kong Waypoint");
|
||||||
|
CPPUNIT_ASSERT_EQUAL(poi3->ident(), std::string{"TEST_WP0"});
|
||||||
|
CPPUNIT_ASSERT_EQUAL(poi3->name(), std::string{"Lovely Hong Kong Waypoint"});
|
||||||
|
CPPUNIT_ASSERT(FGTestApi::geodsApproximatelyEqual(pos3, poi3->geod()));
|
||||||
|
|
||||||
|
// create same ident but nearby
|
||||||
|
SGGeod pos4 = SGGeodesy::direct(egccPos, 270.0, 10.0 * SG_NM_TO_METER);
|
||||||
|
auto duplicatePoi = FGPositioned::createWaypoint(FGPositioned::WAYPOINT,
|
||||||
|
"TEST_WP0", pos4, false, "Lovely Waypoint");
|
||||||
|
CPPUNIT_ASSERT_EQUAL(duplicatePoi, poi);
|
||||||
|
|
||||||
|
// create with invalid type
|
||||||
|
CPPUNIT_ASSERT_THROW(FGPositioned::createWaypoint(FGPositioned::VOR,
|
||||||
|
"TEST_WP99", offsetPos),
|
||||||
|
std::logic_error);
|
||||||
|
|
||||||
|
|
||||||
|
FGPositioned::TypeFilter filt(FGPositioned::WAYPOINT);
|
||||||
|
FGPositionedList wps = FGPositioned::findAllWithIdent("TEST_WP0", &filt);
|
||||||
|
CPPUNIT_ASSERT_EQUAL(wps.size(), static_cast<size_t>(2));
|
||||||
|
|
||||||
|
CPPUNIT_ASSERT(FGPositioned::deleteWaypoint(poi));
|
||||||
|
wps = FGPositioned::findAllWithIdent("TEST_WP0", &filt);
|
||||||
|
CPPUNIT_ASSERT_EQUAL(wps.size(), static_cast<size_t>(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
void NavaidsTests::testTemporaryWaypoint()
|
||||||
|
{
|
||||||
|
SGGeod egccPos = SGGeod::fromDeg(-2.27, 53.35);
|
||||||
|
SGGeod offsetPos = SGGeodesy::direct(egccPos, 45.0, 5.0 * SG_NM_TO_METER);
|
||||||
|
auto poi = FGPositioned::createWaypoint(FGPositioned::WAYPOINT,
|
||||||
|
"TEST_WP_TEMP0", offsetPos, true, "Lovely Waypoint");
|
||||||
|
CPPUNIT_ASSERT(poi);
|
||||||
|
CPPUNIT_ASSERT_EQUAL(poi->ident(), std::string{"TEST_WP_TEMP0"});
|
||||||
|
CPPUNIT_ASSERT_EQUAL(poi->name(), std::string{"Lovely Waypoint"});
|
||||||
|
CPPUNIT_ASSERT(poi->guid() < 0);
|
||||||
|
CPPUNIT_ASSERT_EQUAL(poi->type(), FGPositioned::WAYPOINT);
|
||||||
|
CPPUNIT_ASSERT(FGTestApi::geodsApproximatelyEqual(offsetPos, poi->geod()));
|
||||||
|
|
||||||
|
FGPositioned::TypeFilter filt(FGPositioned::WAYPOINT);
|
||||||
|
FGPositionedList wps = FGPositioned::findAllWithIdent("TEST_WP_TEMP0", &filt);
|
||||||
|
CPPUNIT_ASSERT_EQUAL(wps.size(), static_cast<size_t>(1));
|
||||||
|
CPPUNIT_ASSERT_EQUAL(wps.front(), poi);
|
||||||
|
|
||||||
|
// check temporary points are found spatially
|
||||||
|
auto closest = FGPositioned::findClosestN(egccPos, 1, 50.0, &filt);
|
||||||
|
CPPUNIT_ASSERT_EQUAL(closest.size(), static_cast<size_t>(1));
|
||||||
|
CPPUNIT_ASSERT_EQUAL(closest.front(), poi);
|
||||||
|
|
||||||
|
auto byName = FGPositioned::findAllWithName("lovely", &filt, false);
|
||||||
|
CPPUNIT_ASSERT_EQUAL(byName.size(), static_cast<size_t>(1));
|
||||||
|
CPPUNIT_ASSERT_EQUAL(byName.front(), poi);
|
||||||
|
|
||||||
|
|
||||||
|
// check for gross position update working
|
||||||
|
FGAirportRef vhhh = FGAirport::getByIdent("VHHH");
|
||||||
|
// auto asPoi = fgpositioned_cast<POI>(poi);
|
||||||
|
SGGeod newOffsetPos = SGGeodesy::direct(vhhh->geod(), 10.0, 10.0 * SG_NM_TO_METER);
|
||||||
|
// asPoi->modifyPosition(newOffsetPos);
|
||||||
|
flightgear::NavDataCache::instance()->updatePosition(poi->guid(), newOffsetPos);
|
||||||
|
|
||||||
|
CPPUNIT_ASSERT(FGTestApi::geodsApproximatelyEqual(newOffsetPos, poi->geod()));
|
||||||
|
|
||||||
|
closest = FGPositioned::findClosestN(egccPos, 1, 50.0, &filt);
|
||||||
|
CPPUNIT_ASSERT_EQUAL(closest.size(), static_cast<size_t>(0));
|
||||||
|
|
||||||
|
closest = FGPositioned::findClosestN(vhhh->geod(), 1, 50.0, &filt);
|
||||||
|
CPPUNIT_ASSERT_EQUAL(closest.size(), static_cast<size_t>(1));
|
||||||
|
CPPUNIT_ASSERT_EQUAL(closest.front(), poi);
|
||||||
|
|
||||||
|
// delete it
|
||||||
|
CPPUNIT_ASSERT(FGPositioned::deleteWaypoint(poi));
|
||||||
|
wps = FGPositioned::findAllWithIdent("TEST_WP_TEMP0", &filt);
|
||||||
|
CPPUNIT_ASSERT_EQUAL(wps.size(), static_cast<size_t>(0));
|
||||||
|
|
||||||
|
closest = FGPositioned::findClosestN(vhhh->geod(), 1, 50.0, &filt);
|
||||||
|
CPPUNIT_ASSERT_EQUAL(closest.size(), static_cast<size_t>(0));
|
||||||
|
}
|
||||||
|
|
|
@ -32,6 +32,8 @@ class NavaidsTests : public CppUnit::TestFixture
|
||||||
// Set up the test suite.
|
// Set up the test suite.
|
||||||
CPPUNIT_TEST_SUITE(NavaidsTests);
|
CPPUNIT_TEST_SUITE(NavaidsTests);
|
||||||
CPPUNIT_TEST(testBasic);
|
CPPUNIT_TEST(testBasic);
|
||||||
|
CPPUNIT_TEST(testCustomWaypoint);
|
||||||
|
CPPUNIT_TEST(testTemporaryWaypoint);
|
||||||
CPPUNIT_TEST_SUITE_END();
|
CPPUNIT_TEST_SUITE_END();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -43,6 +45,8 @@ public:
|
||||||
|
|
||||||
// The tests.
|
// The tests.
|
||||||
void testBasic();
|
void testBasic();
|
||||||
|
void testCustomWaypoint();
|
||||||
|
void testTemporaryWaypoint();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // _FG_NAVAIDS_UNIT_TESTS_HXX
|
#endif // _FG_NAVAIDS_UNIT_TESTS_HXX
|
||||||
|
|
Loading…
Add table
Reference in a new issue