#include <map>
#include <set>
-#include <algorithm>
+#include <algorithm> // for sort
+#include <locale> // for char-traits toupper
+
+#include <iostream>
#include <simgear/math/sg_geodesy.hxx>
+#include <simgear/timing/timestamp.hxx>
#include "positioned.hxx"
addToIndices(FGPositioned* aPos)
{
assert(aPos);
- global_namedIndex.insert(global_namedIndex.begin(),
- std::make_pair(aPos->ident(), aPos));
+ if (!aPos->ident().empty()) {
+ global_namedIndex.insert(global_namedIndex.begin(),
+ std::make_pair(aPos->ident(), aPos));
+ }
SpatialPositionedIndex::iterator it = bucketEntryForPositioned(aPos);
it->second.insert(aPos);
{
assert(aPos);
- NamedPositionedIndex::iterator it = global_namedIndex.find(aPos->ident());
- while (it != global_namedIndex.end() && (it->first == aPos->ident())) {
- if (it->second == aPos) {
- global_namedIndex.erase(it);
- break;
- }
-
- ++it;
+ if (!aPos->ident().empty()) {
+ NamedPositionedIndex::iterator it = global_namedIndex.find(aPos->ident());
+ while (it != global_namedIndex.end() && (it->first == aPos->ident())) {
+ if (it->second == aPos) {
+ global_namedIndex.erase(it);
+ break;
+ }
+
+ ++it;
+ } // of multimap walk
}
SpatialPositionedIndex::iterator sit = bucketEntryForPositioned(aPos);
{
public:
RangePredictate(const SGGeod& aOrigin, double aRange) :
- mOrigin(aOrigin),
- mRange(aRange)
+ mOrigin(SGVec3d::fromGeod(aOrigin)),
+ mRangeSqr(aRange * aRange)
{ ; }
bool operator()(const FGPositionedRef& aPos)
{
- double d, az1, az2;
- SGGeodesy::inverse(aPos->geod(), mOrigin, az1, az2, d);
- return (d > mRange);
+ double dSqr = distSqr(aPos->cart(), mOrigin);
+ return (dSqr > mRangeSqr);
}
private:
- SGGeod mOrigin;
- double mRange;
+ SGVec3d mOrigin;
+ double mRangeSqr;
};
static void
{
public:
DistanceOrdering(const SGGeod& aPos) :
- mPos(aPos)
+ mPos(SGVec3d::fromGeod(aPos))
{ }
bool operator()(const FGPositionedRef& a, const FGPositionedRef& b) const
{
- double dA, dB, az1, az2;
- SGGeodesy::inverse(mPos, a->geod(), az1, az2, dA);
- SGGeodesy::inverse(mPos, b->geod(), az1, az2, dB);
+ double dA = distSqr(a->cart(), mPos),
+ dB = distSqr(b->cart(), mPos);
return dA < dB;
}
private:
- SGGeod mPos;
+ SGVec3d mPos;
};
static void
double minDist = HUGE_VAL;
FGPositionedRef result;
NamedPositionedIndex::const_iterator it = range.first;
-
+ SGVec3d cartOrigin(SGVec3d::fromGeod(aOrigin));
+
for (; it != range.second; ++it) {
- // filter by type
- FGPositioned::Type ty = it->second->type();
if (aFilter && !aFilter->pass(range.first->second)) {
continue;
}
// find distance
- double d, az1, az2;
- SGGeodesy::inverse(aOrigin, it->second->geod(), az2, az2, d);
- if (d < minDist) {
- minDist = d;
+ double d2 = distSqr(cartOrigin, it->second->cart());
+ if (d2 < minDist) {
+ minDist = d2;
result = it->second;
}
}
result.insert(result.end(), hits.begin(), hits.end()); // append
} // of outer loop
+ sortByDistance(aPos, result);
if (result.size() > aN) {
result.resize(aN); // truncate at requested number of matches
}
- sortByDistance(aPos, result);
return result;
}
-///////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
-FGPositioned::FGPositioned(Type ty, const std::string& aIdent, double aLat, double aLon, double aElev) :
- mType(ty),
- mPosition(SGGeod::fromDegFt(aLon, aLat, aElev)),
- mIdent(aIdent)
+class OrderByName
{
- addToIndices(this);
- SGReferenced::get(this); // hold an owning ref, for the moment
+public:
+ bool operator()(FGPositioned* a, FGPositioned* b) const
+ {
+ return a->name() < b->name();
+ }
+};
+
+/**
+ * A special purpose helper (imported by FGAirport::searchNamesAndIdents) to
+ * implement the AirportList dialog. It's unfortunate that it needs to reside
+ * here, but for now it's least ugly solution.
+ */
+char** searchAirportNamesAndIdents(const std::string& aFilter)
+{
+ const std::ctype<char> &ct = std::use_facet<std::ctype<char> >(std::locale());
+ std::string filter(aFilter);
+ bool hasFilter = !filter.empty();
+ if (hasFilter) {
+ ct.toupper((char *)filter.data(), (char *)filter.data() + filter.size());
+ }
+
+ NamedPositionedIndex::const_iterator it = global_namedIndex.begin();
+ NamedPositionedIndex::const_iterator end = global_namedIndex.end();
+
+ // note this is a vector of raw pointers, not smart pointers, because it
+ // may get very large and smart-pointer-atomicity-locking then becomes a
+ // bottleneck for this case.
+ std::vector<FGPositioned*> matches;
+ std::string upper;
+
+ for (; it != end; ++it) {
+ FGPositioned::Type ty = it->second->type();
+ if ((ty < FGPositioned::AIRPORT) || (ty > FGPositioned::SEAPORT)) {
+ continue;
+ }
+
+ if (hasFilter && (it->second->ident().find(aFilter) == std::string::npos)) {
+ upper = it->second->name(); // string copy, sadly
+ ct.toupper((char *)upper.data(), (char *)upper.data() + upper.size());
+ if (upper.find(aFilter) == std::string::npos) {
+ continue;
+ }
+ }
+
+ matches.push_back(it->second);
+ }
+
+ // sort alphabetically on name
+ std::sort(matches.begin(), matches.end(), OrderByName());
+
+ // convert results to format comptible with puaList
+ unsigned int numMatches = matches.size();
+ char** result = new char*[numMatches + 1];
+ result[numMatches] = NULL; // end-of-list marker
+
+ // nasty code to avoid excessive string copying and allocations.
+ // We format results as follows (note whitespace!):
+ // ' name-of-airport-chars (ident)'
+ // so the total length is:
+ // 1 + strlen(name) + 4 + 4 (for the ident) + 1 + 1 (for the null)
+ // which gives a grand total of 11 + the length of the name.
+ // note the ident is sometimes only three letters for non-ICAO small strips
+ for (unsigned int i=0; i<numMatches; ++i) {
+ int nameLength = matches[i]->name().size();
+ int icaoLength = matches[i]->ident().size();
+ char* entry = new char[nameLength + 11];
+ char* dst = entry;
+ *dst++ = ' ';
+ memcpy(dst, matches[i]->name().c_str(), nameLength);
+ dst += nameLength;
+ *dst++ = ' ';
+ *dst++ = ' ';
+ *dst++ = ' ';
+ *dst++ = '(';
+ memcpy(dst, matches[i]->ident().c_str(), icaoLength);
+ dst += icaoLength;
+ *dst++ = ')';
+ *dst++ = 0;
+ result[i] = entry;
+ }
+
+ return result;
}
-FGPositioned::FGPositioned(Type ty, const std::string& aIdent, const SGGeod& aPos) :
+///////////////////////////////////////////////////////////////////////////////
+
+FGPositioned::FGPositioned(Type ty, const std::string& aIdent, const SGGeod& aPos, bool aIndexed) :
mType(ty),
mPosition(aPos),
mIdent(aIdent)
-{
- addToIndices(this);
+{
SGReferenced::get(this); // hold an owning ref, for the moment
+
+ if (aIndexed) {
+ assert(ty != TAXIWAY);
+ addToIndices(this);
+ }
}
FGPositioned::~FGPositioned()
{
+ //std::cout << "destroying:" << mIdent << "/" << nameForType(mType) << std::endl;
removeFromIndices(this);
}
return SGBucket(mPosition);
}
+SGVec3d
+FGPositioned::cart() const
+{
+ return SGVec3d::fromGeod(mPosition);
+}
+
const char* FGPositioned::nameForType(Type aTy)
{
switch (aTy) {
+ case RUNWAY: return "runway";
+ case TAXIWAY: return "taxiway";
+ case PARK_STAND: return "parking stand";
case FIX: return "fix";
case VOR: return "VOR";
case NDB: return "NDB";
+ case ILS: return "ILS";
+ case LOC: return "localiser";
+ case GS: return "glideslope";
case OM: return "outer-marker";
case MM: return "middle-marker";
case IM: return "inner-marker";
case HELIPORT: return "heliport";
case SEAPORT: return "seaport";
case WAYPOINT: return "waypoint";
+ case DME: return "dme";
+ case TACAN: return "tacan";
default:
return "unknown";
}