-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_identIndex.begin();
- NamedPositionedIndex::const_iterator end = global_identIndex.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;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-bool
-FGPositioned::Filter::hasTypeRange() const
-{
- assert(minType() <= maxType());
- return (minType() != INVALID) && (maxType() != INVALID);
-}
-
-bool
-FGPositioned::Filter::passType(Type aTy) const
-{
- assert(hasTypeRange());
- return (minType() <= aTy) && (maxType() >= aTy);
-}
-
-static FGPositioned::List
-findAllSortedByRange(const NamedPositionedIndex& aIndex,
- const std::string& aName, const SGGeod& aPos, FGPositioned::Filter* aFilter)
-{
- FGPositioned::List result;
- NamedIndexRange range = aIndex.equal_range(aName);
- for (; range.first != range.second; ++range.first) {
- FGPositioned* candidate = range.first->second;
- if (aFilter) {
- if (aFilter->hasTypeRange() && !aFilter->passType(candidate->type())) {
- continue;
- }
-
- if (!aFilter->pass(candidate)) {
- continue;
- }
- }
-
- result.push_back(range.first->second);
- }
-
- sortByDistance(aPos, result);
- return result;
-}
-
-static FGPositionedRef
-findWithPartial(const NamedPositionedIndex& aIndex, const std::string& aName,
- FGPositioned::Filter* aFilter, int aOffset, bool& aNext)
-{
- // see comment in findNextWithPartialId concerning upperBoundId
- std::string upperBoundId = aName;
- upperBoundId[upperBoundId.size()-1]++;
- NamedPositionedIndex::const_iterator upperBound = aIndex.lower_bound(upperBoundId);
-
- NamedIndexRange range = aIndex.equal_range(aName);
- FGPositionedRef result;
-
- while (range.first != upperBound) {
- for (; range.first != range.second; ++range.first) {
- FGPositionedRef candidate = range.first->second;
- if (aFilter) {
- if (aFilter->hasTypeRange() && !aFilter->passType(candidate->type())) {
- continue;
- }
-
- if (!aFilter->pass(candidate)) {
- continue;
- }
- }
-
- if (result) {
- aNext = true;
- return result;
- } else if (aOffset == 0) {
- // okay, found our result. we need to go around once more to set aNext
- result = candidate;
- } else {
- --aOffset; // seen one more valid result, decrement the count
- }
- }
-
- // Unable to match the filter with this range - try the next range.
- range = aIndex.equal_range(range.second->first);
- }
-
- // if we fell out, we reached the end of the valid range. We might have a
- // valid result, but we definitiely don't have a next result.
- aNext = false;
- return result;
-}