-class DistanceOrdering
-{
-public:
- DistanceOrdering(const SGGeod& aPos) :
- mPos(SGVec3d::fromGeod(aPos))
- { }
-
- bool operator()(const FGPositionedRef& a, const FGPositionedRef& b) const
- {
- double dA = distSqr(a->cart(), mPos),
- dB = distSqr(b->cart(), mPos);
- return dA < dB;
- }
-
-private:
- SGVec3d mPos;
-};
-
-static void
-sortByDistance(const SGGeod& aPos, FGPositioned::List& aResult)
-{
- std::sort(aResult.begin(), aResult.end(), DistanceOrdering(aPos));
-}
-
-static FGPositionedRef
-namedFindClosest(const std::string& aIdent, const SGGeod& aOrigin, FGPositioned::Filter* aFilter)
-{
- NamedIndexRange range = global_namedIndex.equal_range(aIdent);
- if (range.first == range.second) {
- return NULL;
- }
-
-// common case, only one result. looks a bit ugly because these are
-// sequential iterators, not random-access ones
- NamedPositionedIndex::const_iterator check = range.first;
- if (++check == range.second) {
- // excellent, only one match in the range
- FGPositioned* r = range.first->second;
- if (aFilter) {
- if (aFilter->hasTypeRange() && !aFilter->passType(r->type())) {
- return NULL;
- }
-
- if (!aFilter->pass(r)) {
- return NULL;
- }
- } // of have a filter
-
- return r;
- } // of short-circuit logic for single-element range
-
-// multiple matches, we need to actually check the distance to each one
- double minDist = HUGE_VAL;
- FGPositionedRef result;
- NamedPositionedIndex::const_iterator it = range.first;
- SGVec3d cartOrigin(SGVec3d::fromGeod(aOrigin));
-
- for (; it != range.second; ++it) {
- FGPositioned* r = it->second;
- if (aFilter) {
- if (aFilter->hasTypeRange() && !aFilter->passType(r->type())) {
- continue;
- }
-
- if (!aFilter->pass(r)) {
- continue;
- }
- }
-
- // find distance
- double d2 = distSqr(cartOrigin, r->cart());
- if (d2 < minDist) {
- minDist = d2;
- result = r;
- }
- }
-
- return result;
-}
-
-static FGPositioned::List
-spatialGetClosest(const SGGeod& aPos, unsigned int aN, double aCutoffNm, FGPositioned::Filter* aFilter)
-{
- FGPositioned::List result;
- int radius = 1; // start at 1, radius 0 is handled explicitly
- SGBucket buck;
- double lat = aPos.getLatitudeDeg(),
- lon = aPos.getLongitudeDeg();
- // final cutoff is in metres, and scaled to account for testing the corners
- // of the 'box' instead of the centre of each edge
- double cutoffM = aCutoffNm * SG_NM_TO_METER * 1.5;
-
- // base case, simplifes loop to do it seperately here
- spatialFilterInBucket(sgBucketOffset(lon, lat, 0, 0), aFilter, result);
-
- for (;result.size() < aN; ++radius) {
- // cutoff check
- double az1, az2, d1, d2;
- SGGeodesy::inverse(aPos, sgBucketOffset(lon, lat, -radius, -radius).get_center(), az1, az2, d1);
- SGGeodesy::inverse(aPos, sgBucketOffset(lon, lat, radius, radius).get_center(), az1, az2, d2);
-
- if ((d1 > cutoffM) && (d2 > cutoffM)) {
- //std::cerr << "spatialGetClosest terminating due to range cutoff" << std::endl;
- break;
- }
-
- FGPositioned::List hits;
- for ( int i=-radius; i<=radius; i++) {
- spatialFilterInBucket(sgBucketOffset(lon, lat, i, -radius), aFilter, hits);
- spatialFilterInBucket(sgBucketOffset(lon, lat, -radius, i), aFilter, hits);
- spatialFilterInBucket(sgBucketOffset(lon, lat, i, radius), aFilter, hits);
- spatialFilterInBucket(sgBucketOffset(lon, lat, radius, i), aFilter, hits);
- }
-
- 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
- }
-
- return result;
-}
-
-//////////////////////////////////////////////////////////////////////////////
-
-class OrderByName
-{
-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;
-}