bool
TileSource::hasDataForFallback(const osgEarth::TileKey& key) const
{
    //sematics: might have data.

    if ( !key.valid() )
        return false;

    //If no data extents are provided, just return true
    if (_dataExtents.size() == 0) 
        return true;

    const osgEarth::GeoExtent& keyExtent = key.getExtent();
    bool intersectsData = false;

    for (DataExtentList::const_iterator itr = _dataExtents.begin(); itr != _dataExtents.end(); ++itr)
    {
        if ((keyExtent.intersects( *itr )) && 
            (!itr->minLevel().isSet() || itr->minLevel() <= key.getLOD()))
        {
            intersectsData = true;
            break;
        }
    }

    return intersectsData;
}
bool
TileSource::hasData(const osgEarth::TileKey& key) const
{
    //sematics: "might have data"

    if ( !key.valid() )
        return false;

    // If no data extents are provided, and there's no data level override,
    // return true because there might be data but there's no way to tell.
    if (_dataExtents.size() == 0 && !_options.maxDataLevel().isSet())
    {
        return true;
    }

    unsigned int lod = key.getLevelOfDetail();

    // Remap the lod to an appropriate lod if it's not in the same SRS        
    if (!key.getProfile()->isHorizEquivalentTo( getProfile() ) )
    {        
        lod = getProfile()->getEquivalentLOD( key.getProfile(), key.getLevelOfDetail() );        
    }

    // If there's an explicit LOD override and we've exceeded it, no data.
    if (_options.maxDataLevel().isSet() && lod > _options.maxDataLevel().value())
    {
        return false;
    }

    // If there are no extents to check, there might be data.
    if (_dataExtents.size() == 0)
    {
        return true;
    }

    bool intersectsData = false;
    const osgEarth::GeoExtent& keyExtent = key.getExtent();
    
    for (DataExtentList::const_iterator itr = _dataExtents.begin(); itr != _dataExtents.end(); ++itr)
    {
        if ((keyExtent.intersects( *itr )) && 
            (!itr->minLevel().isSet() || itr->minLevel() <= lod ) &&
            (!itr->maxLevel().isSet() || itr->maxLevel() >= lod ))
        {
            intersectsData = true;
            break;
        }
    }

    return intersectsData;
}
bool
TileSource::getBestAvailableTileKey(const osgEarth::TileKey& key,
                                    osgEarth::TileKey&       output) const
{
    // trivial reject
    if ( !key.valid() )
        return false;

    // trivial accept: no data extents = not enough info.
    if (_dataExtents.size() == 0)
    {
        output = key;
        return true;
    }

    // trivial reject: key doesn't intersect the union of data extents at all.
    if ( !getDataExtentsUnion().intersects(key.getExtent()) )
    {
        return false;
    }

    bool     intersects = false;
    unsigned highestLOD = 0;

    // We must use the equivalent lod b/c the key can be in any profile.
    int layerLOD = getProfile()->getEquivalentLOD( key.getProfile(), key.getLOD() );
    
    for (DataExtentList::const_iterator itr = _dataExtents.begin(); itr != _dataExtents.end(); ++itr)
    {
        // check for 2D intersection:
        if (key.getExtent().intersects( *itr ))
        {
            // check that the extent isn't higher-resolution than our key:
            if ( !itr->minLevel().isSet() || layerLOD >= itr->minLevel().get() )
            {
                // Got an intersetion; now test the LODs:
                intersects = true;
                
                // Is the high-LOD set? If not, there's not enough information
                // so just assume our key might be good.
                if ( itr->maxLevel().isSet() == false )
                {
                    output = key;
                    return true;
                }

                // Is our key at a lower or equal LOD than the max key in this extent?
                // If so, our key is good.
                else if ( layerLOD <= itr->maxLevel().get() )
                {
                    output = key;
                    return true;
                }

                // otherwise, record the highest encountered LOD that
                // intersects our key.
                else if ( itr->maxLevel().get() > highestLOD )
                {
                    highestLOD = itr->maxLevel().get();
                }
            }
        }
    }

    if ( intersects )
    {
        output = key.createAncestorKey( highestLOD );
        return true;
    }
    else
    {
        return false;
    }
}