# include <config.h>
#endif
+#include <boost/foreach.hpp>
+#include <algorithm>
+
#include <simgear/debug/logstream.hxx>
#include <simgear/math/sg_geodesy.hxx>
#include <simgear/sg_inlines.h>
#include <Airports/runways.hxx>
-#include <algorithm>
-
using std::string;
+namespace { // anonymous
+
+class NavRecordDistanceSortPredicate
+{
+public:
+ NavRecordDistanceSortPredicate( const SGGeod & position ) :
+ _position(SGVec3d::fromGeod(position)) {}
+
+ bool operator()( const nav_rec_ptr & n1, const nav_rec_ptr & n2 )
+ {
+ if( n1 == NULL || n2 == NULL ) return false;
+ return distSqr(n1->cart(), _position) < distSqr(n2->cart(), _position);
+ }
+private:
+ SGVec3d _position;
+
+};
+
+} // of anonymous namespace
+
// FGNavList ------------------------------------------------------------------
+
+FGNavList::TypeFilter::TypeFilter(const FGPositioned::Type type)
+{
+ if (type == FGPositioned::INVALID) {
+ _mintype = FGPositioned::VOR;
+ _maxtype = FGPositioned::GS;
+ } else {
+ _mintype = _maxtype = type;
+ }
+}
+
+
FGNavList::FGNavList( void )
{
}
return findNavFromList( position, stations );
}
-class TypeFilter : public FGPositioned::Filter
+nav_list_type FGNavList::findAllByFreq( double freq, const SGGeod& position, const FGPositioned::Type type)
{
-public:
- TypeFilter( const FGPositioned::Type mintype, const FGPositioned::Type maxtype ) : _mintype(mintype), _maxtype(maxtype) {}
-
- virtual FGPositioned::Type minType() const {
- return _mintype;
+ nav_list_type stations;
+ TypeFilter filter(type);
+
+ BOOST_FOREACH(nav_rec_ptr nav, navaids[(int)(freq*100.0 + 0.5)]) {
+ if (filter.pass(nav.ptr())) {
+ stations.push_back(nav);
+ }
}
+
+ NavRecordDistanceSortPredicate sortPredicate( position );
+ std::sort( stations.begin(), stations.end(), sortPredicate );
+ return stations;
+}
- virtual FGPositioned::Type maxType() const {
- return _maxtype;
- }
-private:
- FGPositioned::Type _mintype;
- FGPositioned::Type _maxtype;
-};
// Given an Ident and optional freqency, return the first matching
// station.
const nav_list_type FGNavList::findByIdentAndFreq(const string& ident, const double freq, const FGPositioned::Type type )
{
FGPositionedRef cur;
- TypeFilter filter(
- type == FGPositioned::INVALID ? FGPositioned::VOR : type,
- type == FGPositioned::INVALID ? FGPositioned::NDB : type );
+ TypeFilter filter(type);
nav_list_type reply;
cur = FGPositioned::findNextWithPartialId(cur, ident, &filter);
return reply;
}
-class NavRecordDistanceSortPredicate
-{
-public:
- NavRecordDistanceSortPredicate( const SGGeod & position ) :
- _position(SGVec3d::fromGeod(position)) {}
-
- bool operator()( const nav_rec_ptr & n1, const nav_rec_ptr & n2 )
- {
- if( n1 == NULL || n2 == NULL ) return false;
- return distSqr(n1->cart(), _position) < distSqr(n2->cart(), _position);
- }
-private:
- SGVec3d _position;
-
-};
-
// Given an Ident and optional freqency and type ,
// return a list of matching stations sorted by distance to the given position
const nav_list_type FGNavList::findByIdentAndFreq( const SGGeod & position,
const NameTypeEntry names[] = {
{"airport", AIRPORT},
{"vor", VOR},
+ {"loc", LOC},
+ {"ils", ILS},
+ {"gs", GS},
{"ndb", NDB},
{"wpt", WAYPOINT},
{"fix", FIX},
}
}
-FGPositioned::Filter* createSearchFilter(const SGPropertyNode* arg)
-{
- string sty(arg->getStringValue("type", "any"));
- FGPositioned::Type ty = FGPositioned::typeFromName(sty);
- double minRunwayLenFt = arg->getDoubleValue("min-runway-length-ft", -1.0);
-
- if ((ty == FGPositioned::AIRPORT) && (minRunwayLenFt > 0.0)) {
- return new FGAirport::HardSurfaceFilter(minRunwayLenFt);
- } else if (ty != FGPositioned::INVALID) {
- FGPositioned::TypeFilter* tf = new FGPositioned::TypeFilter(ty);
-
- for (int t=1; arg->hasChild("type", t); ++t) {
- sty = arg->getChild("type", t)->getStringValue();
- tf->addType(FGPositioned::typeFromName(sty));
- }
-
- return tf;
- }
-
- return NULL;
-}
-
-static SGGeod commandSearchPos(const SGPropertyNode* arg)
-{
- if (arg->hasChild("longitude-deg") && arg->hasChild("latitude-deg")) {
- return SGGeod::fromDeg(arg->getDoubleValue("longitude-deg"),
- arg->getDoubleValue("latitude-deg"));
- }
-
- // use current viewer/aircraft position
- return SGGeod::fromDeg(fgGetDouble("/position/longitude-deg"),
- fgGetDouble("/position/latitude-deg"));
-}
-
-void commandClearExisting(const SGPropertyNode* arg)
-{
- if (arg->getBoolValue("clear", true)) {
- // delete all existing result children from their parent
- string resultPath = arg->getStringValue("results");
- SGPropertyNode* n = fgGetNode(resultPath.c_str(), 0, true);
- SGPropertyNode* pr = n->getParent();
- pr->removeChildren(n->getName(), false /* keep=false, i.e delete nodes */);
- }
-}
-
-bool commandFindClosest(const SGPropertyNode* arg)
-{
- int n = arg->getIntValue("max-results", 1);
- if ((n < 1) || (n > 100)) {
- SG_LOG(SG_GENERAL, SG_WARN, "commandFindClosest: max-results invalid:" << n);
- return false;
- }
-
- string resultPath = arg->getStringValue("results");
- if (resultPath.empty()) {
- SG_LOG(SG_GENERAL, SG_WARN, "commandFindClosest: no results path defined");
- return false;
- }
-
- std::auto_ptr<FGPositioned::Filter> filt(createSearchFilter(arg));
-// cap search range, since huge ranges will overload everything
- double cutoff = arg->getDoubleValue("cutoff-nm", 400.0);
- SG_CLAMP_RANGE(cutoff, 0.0, 1000.0);
-
- SGGeod pos = commandSearchPos(arg);
- commandClearExisting(arg);
-
- FGPositioned::List results = FGPositioned::findClosestN(pos, n, cutoff, filt.get());
- for (unsigned int i=0; i<results.size(); ++i) {
- SGPropertyNode* resultsNode = fgGetNode(resultPath.c_str(), i, true);
- flightgear::PositionedBinding::bind(results[i], resultsNode);
- }
-
- return true;
-}
-
-bool commandFindByIdent(const SGPropertyNode* arg)
-{
- string resultPath = arg->getStringValue("results");
- if (resultPath.empty()) {
- SG_LOG(SG_GENERAL, SG_WARN, "commandFindByIdent: no results path defined");
- return false;
- }
-
- std::auto_ptr<FGPositioned::Filter> filt(createSearchFilter(arg));
- SGGeod pos = commandSearchPos(arg);
- commandClearExisting(arg);
-
- FGPositioned::List results;
- bool exact = arg->getBoolValue("exact", true);
- if (arg->hasChild("name")) {
- results = FGPositioned::findAllWithName(arg->getStringValue("name"), filt.get(), exact);
- } else if (arg->hasChild("ident")) {
- results = FGPositioned::findAllWithIdent(arg->getStringValue("ident"), filt.get(), exact);
- } else {
- SG_LOG(SG_GENERAL, SG_WARN, "commandFindByIdent: no search term defined");
- return false;
- }
-
- bool orderByRange = arg->getBoolValue("order-by-distance", true);
- if (orderByRange) {
- FGPositioned::sortByRange(results, pos);
- }
-
- for (unsigned int i=0; i<results.size(); ++i) {
- SGPropertyNode* resultsNode = fgGetNode(resultPath.c_str(), i, true);
- flightgear::PositionedBinding::bind(results[i], resultsNode);
- }
-
- return true;
-}
-
-void
-FGPositioned::installCommands()
-{
- SGCommandMgr::instance()->addCommand("find-nearest", commandFindClosest);
- SGCommandMgr::instance()->addCommand("find-by-ident", commandFindByIdent);
-}
-
FGPositioned::TypeFilter::TypeFilter(Type aTy)
{
- types.push_back(aTy);
+ addType(aTy);
}
void FGPositioned::TypeFilter::addType(Type aTy)
{
- types.push_back(aTy);
+ if (aTy == INVALID) {
+ return;
+
+ }
+
+ types.push_back(aTy);
}
bool
FGPositioned::TypeFilter::pass(FGPositioned* aPos) const
{
+ if (types.empty()) {
+ return true;
+ }
+
std::vector<Type>::const_iterator it = types.begin(),
end = types.end();
for (; it != end; ++it) {
#include <Airports/runways.hxx>
#include <Airports/simple.hxx>
+#include <Airports/dynamics.hxx>
+#include <Airports/parking.hxx>
#include <Navaids/navlist.hxx>
#include <Navaids/procedure.hxx>
#include <Main/globals.hxx>
return false;
}
+static int geodFromArgs(naRef* args, int offset, int argc, SGGeod& result)
+{
+ if (offset >= argc) {
+ return 0;
+ }
+
+ if (geodFromHash(args[offset], result)) {
+ return 1;
+ }
+
+ if (((argc - offset) >= 2) && naIsNum(args[offset]) && naIsNum(args[offset + 1])) {
+ double lat = naNumValue(args[0]).num,
+ lon = naNumValue(args[1]).num;
+ result = SGGeod::fromDeg(lon, lat);
+ return 2;
+ }
+
+ return 0;
+}
+
// Convert a cartesian point to a geodetic lat/lon/altitude.
static naRef f_carttogeod(naContext c, naRef me, int argc, naRef* args)
{
AirportInfoFilter() : type(FGPositioned::AIRPORT) {
}
+ bool fromArg(naRef arg)
+ {
+ const char *s = naStr_data(arg);
+ if(!strcmp(s, "airport")) type = FGPositioned::AIRPORT;
+ else if(!strcmp(s, "seaport")) type = FGPositioned::SEAPORT;
+ else if(!strcmp(s, "heliport")) type = FGPositioned::HELIPORT;
+ else
+ return false;
+
+ return true;
+ }
+
virtual FGPositioned::Type minType() const {
return type;
}
// airportinfo(<lat>, <lon> [, <type>]);
static naRef f_airportinfo(naContext c, naRef me, int argc, naRef* args)
{
- SGGeod pos;
+ SGGeod pos = globals->get_aircraft_position();
FGAirport* apt = NULL;
if(argc >= 2 && naIsNum(args[0]) && naIsNum(args[1])) {
pos = SGGeod::fromDeg(args[1].num, args[0].num);
args += 2;
argc -= 2;
- } else {
- pos = globals->get_aircraft_position();
}
double maxRange = 10000.0; // expose this? or pick a smaller value?
if(argc == 0) {
// fall through and use AIRPORT
} else if(argc == 1 && naIsString(args[0])) {
- const char *s = naStr_data(args[0]);
- if(!strcmp(s, "airport")) filter.type = FGPositioned::AIRPORT;
- else if(!strcmp(s, "seaport")) filter.type = FGPositioned::SEAPORT;
- else if(!strcmp(s, "heliport")) filter.type = FGPositioned::HELIPORT;
- else {
+ if (filter.fromArg(args[0])) {
+ // done!
+ } else {
// user provided an <id>, hopefully
- apt = FGAirport::findByIdent(s);
+ apt = FGAirport::findByIdent(naStr_data(args[0]));
if (!apt) {
// return nil here, but don't raise a runtime error; this is a
// legitamate way to validate an ICAO code, for example in a
return hashForAirport(c, apt);
}
+static naRef f_findAirportsWithinRange(naContext c, naRef me, int argc, naRef* args)
+{
+ int argOffset = 0;
+ SGGeod pos = globals->get_aircraft_position();
+ argOffset += geodFromArgs(args, 0, argc, pos);
+
+ if (!naIsNum(args[argOffset])) {
+ naRuntimeError(c, "findAirportsWithinRange expected range (in nm) as arg %d", argOffset);
+ }
+
+ AirportInfoFilter filter; // defaults to airports only
+ double rangeNm = args[argOffset++].num;
+ if (argOffset < argc) {
+ filter.fromArg(args[argOffset++]);
+ }
+
+ naRef r = naNewVector(c);
+
+ FGPositioned::List apts = FGPositioned::findWithinRange(pos, rangeNm, &filter);
+ FGPositioned::sortByRange(apts, pos);
+
+ BOOST_FOREACH(FGPositionedRef a, apts) {
+ FGAirport* apt = (FGAirport*) a.get();
+ naVec_append(r, hashForAirport(c, apt));
+ }
+
+ return r;
+}
+
+static naRef f_findAirportsByICAO(naContext c, naRef me, int argc, naRef* args)
+{
+ if (!naIsString(args[0])) {
+ naRuntimeError(c, "findAirportsByICAO expects string as arg 0");
+ }
+
+ int argOffset = 0;
+ string prefix(naStr_data(args[argOffset++]));
+ AirportInfoFilter filter; // defaults to airports only
+ if (argOffset < argc) {
+ filter.fromArg(args[argOffset++]);
+ }
+
+ naRef r = naNewVector(c);
+
+ FGPositioned::List apts = FGPositioned::findAllWithIdent(prefix, &filter, false);
+
+ BOOST_FOREACH(FGPositionedRef a, apts) {
+ FGAirport* apt = (FGAirport*) a.get();
+ naVec_append(r, hashForAirport(c, apt));
+ }
+
+ return r;
+}
+
static FGAirport* airportFromMe(naRef me)
{
naRef ghost = naHash_cget(me, (char*) "_positioned");
return stars;
}
+static naRef f_airport_parking(naContext c, naRef me, int argc, naRef* args)
+{
+ FGAirport* apt = airportFromMe(me);
+ if (!apt) {
+ naRuntimeError(c, "airport.parking called on non-airport object");
+ }
+
+ naRef r = naNewVector(c);
+ std::string type;
+ bool onlyAvailable = false;
+
+ if (argc > 0 && naIsString(args[0])) {
+ type = naStr_data(args[0]);
+ }
+
+ if ((argc > 1) && naIsNum(args[1])) {
+ onlyAvailable = (args[1].num != 0.0);
+ }
+
+ FGAirportDynamics* dynamics = apt->getDynamics();
+ for (int i=0; i<dynamics->getNrOfParkings(); ++i) {
+ FGParking* park = dynamics->getParking(i);
+ // filter out based on availability and type
+ if (onlyAvailable && !park->isAvailable()) {
+ continue;
+ }
+
+ if (!type.empty() && (park->getType() != type)) {
+ continue;
+ }
+
+ naRef nm = stringToNasal(c, park->getName());
+ naVec_append(r, nm);
+ }
+
+ return r;
+}
+
// Returns vector of data hash for navaid of a <type>, nil on error
// navaids sorted by ascending distance
// navinfo([<lat>,<lon>],[<type>],[<id>])
return reply;
}
+static naRef f_findNavaidsWithinRange(naContext c, naRef me, int argc, naRef* args)
+{
+ int argOffset = 0;
+ SGGeod pos = globals->get_aircraft_position();
+ argOffset += geodFromArgs(args, 0, argc, pos);
+
+ if (!naIsNum(args[argOffset])) {
+ naRuntimeError(c, "findNavaidsWithinRange expected range (in nm) as arg %d", argOffset);
+ }
+
+ FGPositioned::Type type = FGPositioned::INVALID;
+ double rangeNm = args[argOffset++].num;
+ if (argOffset < argc) {
+ type = FGPositioned::typeFromName(naStr_data(args[argOffset]));
+ }
+
+ naRef r = naNewVector(c);
+ FGNavList::TypeFilter filter(type);
+ FGPositioned::List navs = FGPositioned::findWithinRange(pos, rangeNm, &filter);
+ FGPositioned::sortByRange(navs, pos);
+
+ BOOST_FOREACH(FGPositionedRef a, navs) {
+ FGNavRecord* nav = (FGNavRecord*) a.get();
+ naVec_append(r, hashForNavRecord(c, nav, pos));
+ }
+
+ return r;
+}
+
+static naRef f_findNavaidByFrequency(naContext c, naRef me, int argc, naRef* args)
+{
+ int argOffset = 0;
+ SGGeod pos = globals->get_aircraft_position();
+ argOffset += geodFromArgs(args, 0, argc, pos);
+
+ if (!naIsNum(args[argOffset])) {
+ naRuntimeError(c, "findNavaidByFrequency expectes frequency (in Mhz) as arg %d", argOffset);
+ }
+
+ FGPositioned::Type type = FGPositioned::INVALID;
+ double freqMhz = args[argOffset++].num;
+ if (argOffset < argc) {
+ type = FGPositioned::typeFromName(naStr_data(args[argOffset]));
+ }
+
+ nav_list_type navs = globals->get_navlist()->findAllByFreq(freqMhz, pos, type);
+ if (navs.empty()) {
+ return naNil();
+ }
+
+ return hashForNavRecord(c, navs.front().ptr(), pos);
+}
+
+static naRef f_findNavaidsByFrequency(naContext c, naRef me, int argc, naRef* args)
+{
+ int argOffset = 0;
+ SGGeod pos = globals->get_aircraft_position();
+ argOffset += geodFromArgs(args, 0, argc, pos);
+
+ if (!naIsNum(args[argOffset])) {
+ naRuntimeError(c, "findNavaidsByFrequency expectes frequency (in Mhz) as arg %d", argOffset);
+ }
+
+ FGPositioned::Type type = FGPositioned::INVALID;
+ double freqMhz = args[argOffset++].num;
+ if (argOffset < argc) {
+ type = FGPositioned::typeFromName(naStr_data(args[argOffset]));
+ }
+
+ naRef r = naNewVector(c);
+ nav_list_type navs = globals->get_navlist()->findAllByFreq(freqMhz, pos, type);
+
+ BOOST_FOREACH(nav_rec_ptr a, navs) {
+ naVec_append(r, hashForNavRecord(c, a.ptr(), pos));
+ }
+
+ return r;
+}
+
+static naRef f_findNavaidsByIdent(naContext c, naRef me, int argc, naRef* args)
+{
+ int argOffset = 0;
+ SGGeod pos = globals->get_aircraft_position();
+ argOffset += geodFromArgs(args, 0, argc, pos);
+
+ if (!naIsString(args[argOffset])) {
+ naRuntimeError(c, "findNavaidsByIdent expectes ident string as arg %d", argOffset);
+ }
+
+ FGPositioned::Type type = FGPositioned::INVALID;
+ string ident = naStr_data(args[argOffset++]);
+ if (argOffset < argc) {
+ type = FGPositioned::typeFromName(naStr_data(args[argOffset]));
+ }
+
+ naRef r = naNewVector(c);
+ nav_list_type navs = globals->get_navlist()->findByIdentAndFreq(pos, ident, 0.0, type);
+
+ BOOST_FOREACH(nav_rec_ptr a, navs) {
+ naVec_append(r, hashForNavRecord(c, a.ptr(), pos));
+ }
+
+ return r;
+}
+
+
// Convert a cartesian point to a geodetic lat/lon/altitude.
static naRef f_magvar(naContext c, naRef me, int argc, naRef* args)
{
SGGeod pos = globals->get_aircraft_position();
if (argc == 0) {
// fine, use aircraft position
- } else if ((argc == 1) && geodFromHash(args[0], pos)) {
+ } else if (geodFromArgs(args, 0, argc, pos)) {
// okay
- } else if ((argc == 2) && naIsNum(args[0]) && naIsNum(args[1])) {
- double lat = naNumValue(args[0]).num,
- lon = naNumValue(args[1]).num;
- pos = SGGeod::fromDeg(lon, lat);
} else {
naRuntimeError(c, "magvar() expects no arguments, a positioned hash or lat,lon pair");
}
static naRef f_courseAndDistance(naContext c, naRef me, int argc, naRef* args)
{
- SGGeod from = globals->get_aircraft_position(), to;
- if ((argc == 1) && geodFromHash(args[0], to)) {
- // done
- } else if ((argc == 2) && naIsNum(args[0]) && naIsNum(args[1])) {
- // two number arguments, from = current pos, to = lat+lon
- double lat = naNumValue(args[0]).num,
- lon = naNumValue(args[1]).num;
- to = SGGeod::fromDeg(lon, lat);
- } else if ((argc == 2) && geodFromHash(args[0], from) && geodFromHash(args[1], to)) {
- // done
- } else if ((argc == 3) && geodFromHash(args[0], from) && naIsNum(args[1]) && naIsNum(args[2])) {
- double lat = naNumValue(args[1]).num,
- lon = naNumValue(args[2]).num;
- to = SGGeod::fromDeg(lon, lat);
- } else if ((argc == 3) && naIsNum(args[0]) && naIsNum(args[1]) && geodFromHash(args[2], to)) {
- double lat = naNumValue(args[0]).num,
- lon = naNumValue(args[1]).num;
- from = SGGeod::fromDeg(lon, lat);
- } else if (argc == 4) {
- if (!naIsNum(args[0]) || !naIsNum(args[1]) || !naIsNum(args[2]) || !naIsNum(args[3])) {
- naRuntimeError(c, "invalid arguments to courseAndDistance - expected four numbers");
- }
-
- from = SGGeod::fromDeg(naNumValue(args[1]).num, naNumValue(args[0]).num);
- to = SGGeod::fromDeg(naNumValue(args[3]).num, naNumValue(args[2]).num);
+ SGGeod from = globals->get_aircraft_position(), to, p;
+ int argOffset = geodFromArgs(args, 0, argc, p);
+ if (geodFromArgs(args, argOffset, argc, to)) {
+ from = p; // we parsed both FROM and TO args, so first was from
} else {
+ to = p; // only parsed one arg, so FROM is current
+ }
+
+ if (argOffset == 0) {
naRuntimeError(c, "invalid arguments to courseAndDistance");
}
static naRef f_tilePath(naContext c, naRef me, int argc, naRef* args)
{
SGGeod pos = globals->get_aircraft_position();
- if (argc == 0) {
- // fine, use aircraft position
- } else if ((argc == 1) && geodFromHash(args[0], pos)) {
- // okay
- } else if ((argc == 2) && naIsNum(args[0]) && naIsNum(args[1])) {
- double lat = naNumValue(args[0]).num,
- lon = naNumValue(args[1]).num;
- pos = SGGeod::fromDeg(lon, lat);
- } else {
- naRuntimeError(c, "bucketPath() expects no arguments, a positioned hash or lat,lon pair");
- }
-
+ geodFromArgs(args, 0, argc, pos);
SGBucket b(pos);
return stringToNasal(c, b.gen_base_path());
}
{ "geodtocart", f_geodtocart },
{ "geodinfo", f_geodinfo },
{ "airportinfo", f_airportinfo },
+ { "findAirportsWithinRange", f_findAirportsWithinRange },
+ { "findAirportsByICAO", f_findAirportsByICAO },
{ "navinfo", f_navinfo },
+ { "findNavaidsWithinRange", f_findNavaidsWithinRange },
+ { "findNavaidByFrequency", f_findNavaidByFrequency },
+ { "findNavaidsByFrequency", f_findNavaidsByFrequency },
+ { "findNavaidsByID", f_findNavaidsByIdent },
{ "route", f_route },
{ "magvar", f_magvar },
{ "courseAndDistance", f_courseAndDistance },
hashset(c, airportPrototype, "comms", naNewFunc(c, naNewCCode(c, f_airport_comms)));
hashset(c, airportPrototype, "sids", naNewFunc(c, naNewCCode(c, f_airport_sids)));
hashset(c, airportPrototype, "stars", naNewFunc(c, naNewCCode(c, f_airport_stars)));
+ hashset(c, airportPrototype, "parking", naNewFunc(c, naNewCCode(c, f_airport_parking)));
routePrototype = naNewHash(c);
hashset(c, gcSave, "routeProto", routePrototype);