+// convert to a plain list
+ unsigned int count = aResult.size();
+ for (unsigned int i=0; i<count; ++i) {
+ aResult[i] = r[i].get();
+ }
+}
+
+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);
+}
+
+void FGPositioned::TypeFilter::addType(Type aTy)
+{
+ types.push_back(aTy);
+}
+
+bool
+FGPositioned::TypeFilter::pass(FGPositioned* aPos) const
+{
+ std::vector<Type>::const_iterator it = types.begin(),
+ end = types.end();
+ for (; it != end; ++it) {
+ return aPos->type() == *it;
+ }
+
+ return false;