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,
|
||||
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),
|
||||
isOnRunway(aOnRunway),
|
||||
holdType(aHoldType),
|
||||
|
|
|
@ -1333,9 +1333,12 @@ bool FGRouteMgr::commandDefineUserWaypoint(const SGPropertyNode * arg, SGPropert
|
|||
return false;
|
||||
}
|
||||
|
||||
const bool temporary = arg->getBoolValue("temporary");
|
||||
SGGeod pos(SGGeod::fromDeg(arg->getDoubleValue("longitude-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;
|
||||
}
|
||||
|
||||
|
@ -1347,7 +1350,14 @@ bool FGRouteMgr::commandDeleteUserWaypoint(const SGPropertyNode * arg, SGPropert
|
|||
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);
|
||||
FGPositionedRef wpt = FGPositioned::createUserWaypoint(ident, _scratchPos);
|
||||
FGPositionedRef wpt = FGPositioned::createWaypoint(FGPositioned::WAYPOINT, ident, _scratchPos, false);
|
||||
_searchResults.clear();
|
||||
_searchResults.push_back(wpt);
|
||||
setScratchFromPositioned(wpt.get(), -1);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
const int SCHEMA_VERSION = 22;
|
||||
const int SCHEMA_VERSION = 25;
|
||||
|
||||
#define SCHEMA_SQL \
|
||||
"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 INDEX airway_edge_from ON airway_edge(a);" \
|
||||
"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);
|
||||
|
||||
initTemporaryTables();
|
||||
|
||||
readPropertyQuery = prepare("SELECT value FROM properties WHERE key=?");
|
||||
writePropertyQuery = prepare("INSERT INTO properties (key, value) VALUES (?,?)");
|
||||
clearProperty = prepare("DELETE FROM properties WHERE key=?1");
|
||||
|
@ -549,6 +551,18 @@ public:
|
|||
} // 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()
|
||||
{
|
||||
writePropertyMulti = prepare("INSERT INTO properties (key, value) VALUES(?1,?2)");
|
||||
|
@ -558,20 +572,20 @@ public:
|
|||
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"
|
||||
statCacheCheck = prepare("SELECT stamp, sha FROM stat_cache WHERE path=?");
|
||||
stampFileCache = prepare("INSERT OR REPLACE INTO stat_cache "
|
||||
"(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=?");
|
||||
loadNavaid = prepare("SELECT range_nm, freq, multiuse, runway, colocated FROM navaid WHERE rowid=?");
|
||||
loadCommStation = prepare("SELECT freq_khz, range_nm FROM comm WHERE rowid=?");
|
||||
loadRunwayStmt = prepare("SELECT heading, length_ft, width_m, surface, displaced_threshold,"
|
||||
"stopway, reciprocal, ils FROM runway WHERE rowid=?1");
|
||||
|
||||
getAirportItems = prepare("SELECT rowid FROM positioned WHERE airport=?1 " AND_TYPED);
|
||||
getAirportItems = prepare("SELECT guid FROM all_positioned WHERE airport=?1 " AND_TYPED);
|
||||
|
||||
|
||||
setAirportMetar = prepare("UPDATE airport SET has_metar=?2 WHERE rowid="
|
||||
|
@ -588,8 +602,16 @@ public:
|
|||
"cart_x, cart_y, cart_z)"
|
||||
" VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11)");
|
||||
|
||||
setAirportPos = prepare("UPDATE positioned SET lon=?2, lat=?3, elev_m=?4, octree_node=?5, "
|
||||
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");
|
||||
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 (?, ?, ?)");
|
||||
insertNavaid = prepare("INSERT INTO navaid (rowid, freq, range_nm, multiuse, runway, colocated)"
|
||||
" VALUES (?1, ?2, ?3, ?4, ?5, ?6)");
|
||||
|
@ -601,11 +623,11 @@ public:
|
|||
" 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");
|
||||
removePositionedQuery = prepare("DELETE FROM positioned WHERE rowid=?1");
|
||||
removeTempPosQuery = prepare("DELETE FROM temp_positioned WHERE rowid=?1");
|
||||
|
||||
// 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)");
|
||||
findClosestWithIdent = prepare("SELECT guid FROM all_positioned WHERE ident=?1 " AND_TYPED " ORDER BY distanceCartSqr(cart_x, cart_y, cart_z, ?4, ?5, ?6)");
|
||||
|
||||
findCommByFreq = prepare("SELECT positioned.rowid FROM positioned, comm WHERE "
|
||||
"positioned.rowid=comm.rowid AND freq_khz=?1 "
|
||||
|
@ -649,7 +671,7 @@ public:
|
|||
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="
|
||||
"(SELECT rowid FROM positioned WHERE type=?4 AND ident=?1)");
|
||||
|
@ -839,6 +861,36 @@ public:
|
|||
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,
|
||||
FGPositioned::Filter* filter, bool exact)
|
||||
{
|
||||
|
@ -847,7 +899,7 @@ public:
|
|||
|
||||
// build up SQL query text
|
||||
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) {
|
||||
sql += " " AND_TYPED;
|
||||
}
|
||||
|
@ -916,12 +968,12 @@ public:
|
|||
deferredOctreeUpdates.clear();
|
||||
}
|
||||
|
||||
void removePositionedWithIdent(FGPositioned::Type ty, const std::string& aIdent)
|
||||
void removePositioned(PositionedID rowid)
|
||||
{
|
||||
sqlite3_bind_int(removePOIQuery, 1, ty);
|
||||
sqlite_bind_stdstring(removePOIQuery, 2, aIdent);
|
||||
execUpdate(removePOIQuery);
|
||||
reset(removePOIQuery);
|
||||
auto stmt = rowid < 0 ? removeTempPosQuery : removePositionedQuery;
|
||||
sqlite3_bind_int64(stmt, 1, rowid);
|
||||
execUpdate(stmt);
|
||||
reset(stmt);
|
||||
}
|
||||
|
||||
NavDataCache* outer;
|
||||
|
@ -959,9 +1011,10 @@ public:
|
|||
|
||||
sqlite3_stmt_ptr insertPositionedQuery, insertAirport, insertTower, insertRunway,
|
||||
insertCommStation, insertNavaid;
|
||||
sqlite3_stmt_ptr insertTempPosQuery;
|
||||
sqlite3_stmt_ptr setAirportMetar, setRunwayReciprocal, setRunwayILS, setNavaidColocated,
|
||||
setAirportPos;
|
||||
sqlite3_stmt_ptr removePOIQuery;
|
||||
updatePosition, updateTempPos;
|
||||
sqlite3_stmt_ptr removePositionedQuery, removeTempPosQuery;
|
||||
|
||||
sqlite3_stmt_ptr findClosestWithIdent;
|
||||
// octree (spatial index) related queries
|
||||
|
@ -996,6 +1049,10 @@ public:
|
|||
// if we're performing a rebuild, the thread that is doing the work.
|
||||
// otherwise, NULL
|
||||
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::TOWN:
|
||||
case FGPositioned::VILLAGE:
|
||||
{
|
||||
FGPositioned* wpt = new FGPositioned(rowid, ty, ident, pos);
|
||||
case FGPositioned::VISUAL_REPORTING_POINT: {
|
||||
FGPositioned* wpt = new POI(rowid, ty, ident, pos, name);
|
||||
return wpt;
|
||||
}
|
||||
|
||||
|
@ -2009,14 +2066,14 @@ void NavDataCache::clearDynamicPositioneds()
|
|||
|
||||
FGPositionedRef NavDataCache::loadById(PositionedID rowid)
|
||||
{
|
||||
if (rowid < 1) {
|
||||
return NULL;
|
||||
}
|
||||
if (!d) return NULL;
|
||||
if (!d || (rowid == 0)) {
|
||||
return {};
|
||||
};
|
||||
|
||||
PositionedCache::iterator it = d->cache.find(rowid);
|
||||
if (it != d->cache.end()) {
|
||||
d->cacheHits++;
|
||||
return it->second; // cache it
|
||||
return it->second; // cache hit
|
||||
}
|
||||
|
||||
sqlite3_int64 aptId;
|
||||
|
@ -2060,18 +2117,44 @@ PositionedID NavDataCache::insertAirport(FGPositioned::Type ty, const string& id
|
|||
|
||||
void NavDataCache::updatePosition(PositionedID item, const SGGeod &pos)
|
||||
{
|
||||
if (d->cache.find(item) != d->cache.end()) {
|
||||
SG_LOG(SG_NAVCACHE, SG_DEBUG, "updating position of an item in the cache");
|
||||
const bool isTemporary = (item < 0);
|
||||
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);
|
||||
}
|
||||
|
||||
SGVec3d cartPos(SGVec3d::fromGeod(pos));
|
||||
|
||||
sqlite3_bind_int(d->setAirportPos, 1, item);
|
||||
sqlite3_bind_double(d->setAirportPos, 2, pos.getLongitudeDeg());
|
||||
sqlite3_bind_double(d->setAirportPos, 3, pos.getLatitudeDeg());
|
||||
sqlite3_bind_double(d->setAirportPos, 4, pos.getElevationM());
|
||||
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());
|
||||
|
||||
if (!isTemporary) {
|
||||
// bug 905; the octree leaf may change here, but the leaf may already be
|
||||
// loaded, and caching its children. (Either the old or new leaf!). Worse,
|
||||
// we may be called here as a result of loading one of those leaf's children.
|
||||
|
@ -2081,14 +2164,15 @@ void NavDataCache::updatePosition(PositionedID item, const SGGeod &pos)
|
|||
// all the spatial searches ultimately use the items' real cartesian position,
|
||||
// which was updated above.
|
||||
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(d->setAirportPos, 7, cartPos.y());
|
||||
sqlite3_bind_double(d->setAirportPos, 8, cartPos.z());
|
||||
sqlite3_bind_double(stmt, 6, cartPos.x());
|
||||
sqlite3_bind_double(stmt, 7, cartPos.y());
|
||||
sqlite3_bind_double(stmt, 8, cartPos.z());
|
||||
|
||||
|
||||
d->execUpdate(d->setAirportPos);
|
||||
d->execUpdate(stmt);
|
||||
}
|
||||
|
||||
void NavDataCache::insertTower(PositionedID airportId, const SGGeod& pos)
|
||||
|
@ -2215,21 +2299,32 @@ PositionedID NavDataCache::insertCommStation(FGPositioned::Type ty,
|
|||
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);
|
||||
}
|
||||
|
||||
PositionedID NavDataCache::createPOI(FGPositioned::Type ty, const std::string& ident, const SGGeod& aPos)
|
||||
{
|
||||
return d->insertPositioned(ty, ident, string(), aPos, 0,
|
||||
if (isTransient) {
|
||||
return d->insertTemporaryPositioned(createTransientID(), ty, ident, name, aPos,
|
||||
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);
|
||||
// should remove from the live cache too?
|
||||
const bool isTemporary = ref->guid() < 0;
|
||||
// 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;
|
||||
}
|
||||
|
@ -2811,6 +2906,11 @@ SGPath NavDataCache::path() const
|
|||
return d->path;
|
||||
}
|
||||
|
||||
PositionedID NavDataCache::createTransientID()
|
||||
{
|
||||
return d->nextTransientId--;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Transaction RAII object
|
||||
|
||||
|
|
|
@ -182,11 +182,10 @@ public:
|
|||
PositionedID insertCommStation(FGPositioned::Type ty,
|
||||
const std::string& name, const SGGeod& pos, int freq, int range,
|
||||
PositionedID apt);
|
||||
PositionedID insertFix(const std::string& ident, const SGGeod& aPos);
|
||||
|
||||
PositionedID createPOI(FGPositioned::Type ty, const std::string& ident, const SGGeod& aPos);
|
||||
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
|
||||
void setAirportMetar(const std::string& icao, bool hasMetar);
|
||||
|
@ -336,6 +335,8 @@ public:
|
|||
|
||||
void clearDynamicPositioneds();
|
||||
|
||||
PositionedID createTransientID();
|
||||
|
||||
private:
|
||||
NavDataCache();
|
||||
|
||||
|
|
|
@ -1,26 +1,9 @@
|
|||
/**
|
||||
* PositionedOctree - define a spatial octree containing Positioned items
|
||||
* arranged by their global cartesian position.
|
||||
/*
|
||||
* SPDX-FileCopyrightText: (C) 2012 James Turner <james@flightgear.org>
|
||||
* 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 "PositionedOctree.hxx"
|
||||
|
@ -152,6 +135,20 @@ void Leaf::insertChild(FGPositioned::Type ty, PositionedID 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()
|
||||
{
|
||||
if (_childrenLoaded) {
|
||||
|
@ -337,6 +334,7 @@ bool findNearestN(const SGVec3d& aPos, unsigned int aN, double aCutoffM, FGPosit
|
|||
FindNearestPQueue pq;
|
||||
FindNearestResults results;
|
||||
pq.push(Ordered<Node*>(globalPersistentOctree(), 0));
|
||||
pq.push(Ordered<Node*>(globalTransientOctree(), 0));
|
||||
double cut = aCutoffM;
|
||||
|
||||
SGTimeStamp tm;
|
||||
|
@ -378,6 +376,7 @@ bool findAllWithinRange(const SGVec3d& aPos, double aRangeM, FGPositioned::Filte
|
|||
FindNearestPQueue pq;
|
||||
FindNearestResults results;
|
||||
pq.push(Ordered<Node*>(globalPersistentOctree(), 0));
|
||||
pq.push(Ordered<Node*>(globalTransientOctree(), 0));
|
||||
double rng = aRangeM;
|
||||
|
||||
SGTimeStamp tm;
|
||||
|
|
|
@ -1,28 +1,10 @@
|
|||
/**
|
||||
* PositionedOctree - define a spatial octree containing Positioned items
|
||||
* arranged by their global cartesian position.
|
||||
/*
|
||||
* SPDX-FileCopyrightText: (C) 2012 James Turner <james@flightgear.org>
|
||||
* 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.
|
||||
|
||||
#ifndef FG_POSITIONED_OCTREE_HXX
|
||||
#define FG_POSITIONED_OCTREE_HXX
|
||||
#pragma once
|
||||
|
||||
// std
|
||||
#include <array>
|
||||
|
@ -193,6 +175,7 @@ namespace Octree
|
|||
}
|
||||
|
||||
void insertChild(FGPositioned::Type ty, PositionedID id);
|
||||
void removeChild(PositionedID id);
|
||||
|
||||
private:
|
||||
bool _childrenLoaded = false;
|
||||
|
@ -253,4 +236,3 @@ namespace Octree
|
|||
|
||||
} // of namespace flightgear
|
||||
|
||||
#endif // of FG_POSITIONED_OCTREE_HXX
|
||||
|
|
|
@ -422,12 +422,12 @@ void Airway::Network::addEdge(int aWay, const SGGeod& aStartPos,
|
|||
|
||||
if (!start) {
|
||||
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) {
|
||||
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());
|
||||
|
|
|
@ -145,7 +145,7 @@ void FixesLoader::loadFixes(const NavDataCache::SceneryLocation& sceneryLocation
|
|||
}
|
||||
|
||||
if (!duplicate) {
|
||||
_cache->insertFix(ident, pos);
|
||||
_cache->createPOI(FGPositioned::FIX, ident, pos, {}, false);
|
||||
_loadedFixes.insert({ident, pos});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,26 +1,10 @@
|
|||
// poidb.cxx -- points of interest management routines
|
||||
//
|
||||
// Written by Christian Schmitt, March 2013
|
||||
//
|
||||
// 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$
|
||||
/*
|
||||
* SPDX-FileCopyrightText: (C) Christian Schmitt, March 2013
|
||||
* SPDX_FileComment: points of interest management routines
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <istream> // std::ws
|
||||
#include "poidb.hxx"
|
||||
|
@ -45,6 +29,10 @@ mapPOITypeToFGPType(int aTy)
|
|||
case 12: return FGPositioned::CITY;
|
||||
case 13: return FGPositioned::TOWN;
|
||||
case 14: return FGPositioned::VILLAGE;
|
||||
|
||||
case 1000: return FGPositioned::VISUAL_REPORTING_POINT;
|
||||
case 1001: return FGPositioned::WAYPOINT;
|
||||
|
||||
default:
|
||||
throw sg_range_exception("Unknown POI type", "FGNavDataCache::readPOIFromStream");
|
||||
}
|
||||
|
@ -87,7 +75,7 @@ static PositionedID readPOIFromStream(std::istream& aStream, NavDataCache* cache
|
|||
return 0;
|
||||
}
|
||||
|
||||
return cache->createPOI(type, name, pos);
|
||||
return cache->createPOI(type, name, pos, name, false);
|
||||
}
|
||||
|
||||
// load and initialize the POI database
|
||||
|
|
|
@ -1,26 +1,10 @@
|
|||
// poidb.cxx -- points of interest management routines
|
||||
//
|
||||
// Written by Christian Schmitt, March 2013
|
||||
//
|
||||
// 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$
|
||||
/*
|
||||
* SPDX-FileCopyrightText: (C) Christian Schmitt, March 2013
|
||||
* SPDX_FileComment: points of interest management routines
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _FG_POIDB_HXX
|
||||
#define _FG_POIDB_HXX
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
|
@ -37,4 +21,3 @@ bool poiDBInit(const SGPath& path);
|
|||
|
||||
} // of namespace flightgear
|
||||
|
||||
#endif // _FG_NAVDB_HXX
|
||||
|
|
|
@ -1,22 +1,8 @@
|
|||
// positioned.cxx - base class for objects which are positioned
|
||||
//
|
||||
// Copyright (C) 2008 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.
|
||||
//
|
||||
// $Id$
|
||||
/*
|
||||
* SPDX-FileCopyrightText: (C) 2008 James Turner <james@flightgear.org>
|
||||
* SPDX_FileComment: base class for objects which are spatially located in the simulated world
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
|
@ -60,8 +46,6 @@ static bool validateFilter(FGPositioned::Filter* filter)
|
|||
return true;
|
||||
}
|
||||
|
||||
const PositionedID FGPositioned::TRANSIENT_ID = -2;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
FGPositioned::FGPositioned( PositionedID aGuid,
|
||||
|
@ -118,25 +102,55 @@ bool FGPositioned::isNavaidType(FGPositioned* pos)
|
|||
}
|
||||
}
|
||||
|
||||
FGPositionedRef
|
||||
FGPositioned::createUserWaypoint(const std::string& aIdent, const SGGeod& aPos)
|
||||
static bool isValidCustomWaypointType(FGPositioned::Type ty)
|
||||
{
|
||||
NavDataCache* cache = NavDataCache::instance();
|
||||
TypeFilter filter(WAYPOINT);
|
||||
FGPositionedList existing = cache->findAllWithIdent(aIdent, &filter, true);
|
||||
if (!existing.empty()) {
|
||||
SG_LOG(SG_NAVAID, SG_WARN, "attempt to insert duplicate WAYPOINT:" << aIdent);
|
||||
return existing.front();
|
||||
switch (ty) {
|
||||
case FGPositioned::WAYPOINT:
|
||||
case FGPositioned::FIX:
|
||||
case FGPositioned::VISUAL_REPORTING_POINT:
|
||||
case FGPositioned::OBSTACLE:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
PositionedID id = cache->createPOI(WAYPOINT, aIdent, aPos);
|
||||
FGPositionedRef
|
||||
FGPositioned::createWaypoint(FGPositioned::Type aType, const std::string& aIdent, const SGGeod& aPos,
|
||||
bool isTemporary,
|
||||
const std::string& aName)
|
||||
{
|
||||
NavDataCache* cache = NavDataCache::instance();
|
||||
|
||||
if (!isValidCustomWaypointType(aType)) {
|
||||
throw std::logic_error(std::string{"Create waypoint: not allowed for type:"} + nameForType(aType));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
bool FGPositioned::deleteUserWaypoint(const std::string& aIdent)
|
||||
bool FGPositioned::deleteWaypoint(FGPositionedRef ref)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -193,6 +207,7 @@ FGPositioned::Type FGPositioned::typeFromName(const std::string& aName)
|
|||
{"obstacle", OBSTACLE},
|
||||
{"parking", PARKING},
|
||||
{"taxi-node", TAXI_NODE},
|
||||
{"visual-reporting-point", VISUAL_REPORTING_POINT},
|
||||
|
||||
// aliases
|
||||
{"localizer", LOC},
|
||||
|
@ -208,9 +223,9 @@ FGPositioned::Type FGPositioned::typeFromName(const std::string& aName)
|
|||
{"middle-marker", MM},
|
||||
{"inner-marker", IM},
|
||||
{"parking-stand", PARKING},
|
||||
{"vrp", VISUAL_REPORTING_POINT},
|
||||
|
||||
{NULL, INVALID}
|
||||
};
|
||||
{NULL, INVALID}};
|
||||
|
||||
std::string lowerName = simgear::strutils::lowercase(aName);
|
||||
|
||||
|
@ -259,6 +274,9 @@ const char* FGPositioned::nameForType(Type aTy)
|
|||
case CITY: return "city";
|
||||
case TOWN: return "town";
|
||||
case VILLAGE: return "village";
|
||||
case VISUAL_REPORTING_POINT: return "visual-reporting-point";
|
||||
case MOBILE_TACAN: return "mobile-tacan";
|
||||
case OBSTACLE: return "obstacle";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
|
@ -493,3 +511,13 @@ FGPositioned::TypeFilter::pass(FGPositioned* aPos) const
|
|||
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
|
||||
//
|
||||
// Copyright (C) 2008 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.
|
||||
//
|
||||
// $Id$
|
||||
/*
|
||||
* SPDX-FileCopyrightText: (C) 2008 James Turner <james@flightgear.org>
|
||||
* SPDX_FileComment: base class for objects which are spatially located in the simulated world
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef FG_POSITIONED_HXX
|
||||
#define FG_POSITIONED_HXX
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
|
@ -42,8 +27,6 @@ namespace flightgear { class NavDataCache; }
|
|||
class FGPositioned : public SGReferenced
|
||||
{
|
||||
public:
|
||||
static const PositionedID TRANSIENT_ID;
|
||||
|
||||
typedef enum {
|
||||
INVALID = 0,
|
||||
AIRPORT,
|
||||
|
@ -91,7 +74,8 @@ public:
|
|||
CITY,
|
||||
TOWN,
|
||||
VILLAGE,
|
||||
|
||||
VISUAL_REPORTING_POINT,
|
||||
LAST_POI_TYPE,
|
||||
LAST_TYPE
|
||||
} Type;
|
||||
|
||||
|
@ -295,13 +279,16 @@ public:
|
|||
*/
|
||||
static const char* nameForType(Type aTy);
|
||||
|
||||
static FGPositionedRef createUserWaypoint(const std::string& aIdent, const SGGeod& aPos);
|
||||
static bool deleteUserWaypoint(const std::string& aIdent);
|
||||
static FGPositionedRef createWaypoint(FGPositioned::Type aType, const std::string& aIdent, const SGGeod& aPos,
|
||||
bool isTemporary = false,
|
||||
const std::string& aName = {});
|
||||
static bool deleteWaypoint(FGPositionedRef aWpt);
|
||||
protected:
|
||||
friend class flightgear::NavDataCache;
|
||||
|
||||
FGPositioned(PositionedID aGuid, Type ty, const std::string& aIdent, const SGGeod& aPos);
|
||||
|
||||
// called by NavDataCache::updatePosition
|
||||
void modifyPosition(const SGGeod& newPos);
|
||||
void invalidatePosition();
|
||||
|
||||
|
@ -318,6 +305,8 @@ private:
|
|||
const SGVec3d mCart;
|
||||
};
|
||||
|
||||
|
||||
|
||||
template <class T>
|
||||
T* fgpositioned_cast(FGPositioned* p)
|
||||
{
|
||||
|
@ -340,4 +329,18 @@ T* fgpositioned_cast(FGPositionedRef p)
|
|||
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 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)
|
||||
{
|
||||
naRef s = naNewString(c);
|
||||
|
@ -92,7 +95,8 @@ FGPositioned* positionedGhost(naRef r)
|
|||
(naGhost_type(r) == &NavaidGhostType) ||
|
||||
(naGhost_type(r) == &RunwayGhostType) ||
|
||||
(naGhost_type(r) == &FixGhostType) ||
|
||||
(naGhost_type(r) == &CommGhostType)) {
|
||||
(naGhost_type(r) == &FixGhostType) ||
|
||||
(naGhost_type(r) == &POIGhostType)) {
|
||||
return (FGPositioned*) naGhost_ptr(r);
|
||||
}
|
||||
|
||||
|
@ -134,12 +138,22 @@ static FGFix* fixGhost(naRef r)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static flightgear::CommStation* commGhost(naRef r)
|
||||
{
|
||||
if (naGhost_type(r) == &CommGhostType)
|
||||
return (flightgear::CommStation*)naGhost_ptr(r);
|
||||
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)
|
||||
{
|
||||
|
@ -151,6 +165,7 @@ static void positionedGhostDestroy(void* g)
|
|||
|
||||
static naRef airportPrototype;
|
||||
static naRef geoCoordClass;
|
||||
static naRef waypointPrototype;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (!pos) {
|
||||
|
@ -251,6 +276,14 @@ naRef ghostForPositioned(naContext c, FGPositionedRef pos)
|
|||
case FGPositioned::FREQ_ENROUTE:
|
||||
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:
|
||||
SG_LOG(SG_NASAL, SG_DEV_ALERT, "Type lacks Nasal ghost mapping:" << pos->typeString());
|
||||
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()
|
||||
else if (!strcmp(fieldName, "type")) *out = stringToNasal(c, "fix");
|
||||
else if (!strcmp(fieldName, "name")) *out = stringToNasal(c, fix->ident());
|
||||
else if (!strcmp(fieldName, "guid"))
|
||||
*out = naNum(fix->guid());
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
|
@ -433,6 +468,8 @@ static const char* commGhostGetMember(naContext c, void* g, naRef field, naRef*
|
|||
*out = stringToNasal(c, comm->name());
|
||||
else if (!strcmp(fieldName, "frequency")) {
|
||||
*out = naNum(comm->freqMHz());
|
||||
} else if (!strcmp(fieldName, "guid")) {
|
||||
*out = naNum(comm->guid());
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
@ -440,6 +477,33 @@ static const char* commGhostGetMember(naContext c, void* g, naRef field, naRef*
|
|||
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)
|
||||
{
|
||||
|
@ -540,6 +604,11 @@ int geodFromArgs(naRef* args, int offset, int argc, SGGeod& result)
|
|||
return 1;
|
||||
}
|
||||
|
||||
if (gt == &POIGhostType) {
|
||||
result = poiGhost(args[offset])->geod();
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto wp = wayptGhost(args[offset]);
|
||||
if (wp) {
|
||||
result = wp->position();
|
||||
|
@ -1648,6 +1717,79 @@ static naRef f_tileIndex(naContext c, naRef me, int argc, naRef* args)
|
|||
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()
|
||||
{
|
||||
|
@ -1711,6 +1853,8 @@ static struct {
|
|||
{"greatCircleMove", f_greatCircleMove},
|
||||
{"tileIndex", f_tileIndex},
|
||||
{"tilePath", f_tilePath},
|
||||
{"createWaypoint", f_createWaypoint},
|
||||
{"deleteWaypoint", f_deleteWaypoint},
|
||||
{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, "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++) {
|
||||
hashset(c, globals, funcs[i].name,
|
||||
naNewFunc(c, naNewCCode(c, funcs[i].func)));
|
||||
|
|
|
@ -18,8 +18,7 @@
|
|||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef SCRIPTING_NASAL_POSITIONED_HXX
|
||||
#define SCRIPTING_NASAL_POSITIONED_HXX
|
||||
#pragma once
|
||||
|
||||
#include <simgear/nasal/nasal.h>
|
||||
|
||||
|
@ -48,5 +47,3 @@ naRef initNasalPositioned(naRef globals, naContext c);
|
|||
naRef initNasalPositioned_cppbind(naRef globals, naContext c);
|
||||
void postinitNasalPositioned(naRef globals, naContext c);
|
||||
void shutdownNasalPositioned();
|
||||
|
||||
#endif // of SCRIPTING_NASAL_POSITIONED_HXX
|
||||
|
|
|
@ -459,6 +459,12 @@ SGPropertyNode_ptr propsFromString(const std::string& s)
|
|||
return m;
|
||||
}
|
||||
|
||||
bool geodsApproximatelyEqual(const SGGeod& a, const SGGeod& b)
|
||||
{
|
||||
return SGGeodesy::distanceM(a, b) < 50.0;
|
||||
}
|
||||
|
||||
|
||||
namespace tearDown {
|
||||
|
||||
void shutdownTestGlobals()
|
||||
|
|
|
@ -71,6 +71,8 @@ void writePointToKML(const std::string& ident, const SGGeod& pos);
|
|||
|
||||
bool executeNasal(const std::string& code);
|
||||
|
||||
bool geodsApproximatelyEqual(const SGGeod& a, const SGGeod& b);
|
||||
|
||||
namespace tearDown {
|
||||
|
||||
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"}, std::string{m_gpsNode->getStringValue("wp/wp[1]/ID")});
|
||||
|
||||
SG_UNUSED(elapsed);
|
||||
}
|
||||
|
||||
void RNAVProcedureTests::testLFKC_AJO1R()
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
#include "test_suite/FGTestApi/testGlobals.hxx"
|
||||
#include "test_suite/FGTestApi/NavDataCache.hxx"
|
||||
|
||||
#include <Airports/airport.hxx>
|
||||
|
||||
#include <Navaids/NavDataCache.hxx>
|
||||
#include <Navaids/navrecord.hxx>
|
||||
#include <Navaids/navlist.hxx>
|
||||
|
@ -34,3 +36,113 @@ void NavaidsTests::testBasic()
|
|||
CPPUNIT_ASSERT_EQUAL(tla->get_freq(), 11570);
|
||||
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.
|
||||
CPPUNIT_TEST_SUITE(NavaidsTests);
|
||||
CPPUNIT_TEST(testBasic);
|
||||
CPPUNIT_TEST(testCustomWaypoint);
|
||||
CPPUNIT_TEST(testTemporaryWaypoint);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
|
@ -43,6 +45,8 @@ public:
|
|||
|
||||
// The tests.
|
||||
void testBasic();
|
||||
void testCustomWaypoint();
|
||||
void testTemporaryWaypoint();
|
||||
};
|
||||
|
||||
#endif // _FG_NAVAIDS_UNIT_TESTS_HXX
|
||||
|
|
Loading…
Reference in a new issue