osgEarth 2.1.1
Public Member Functions | Protected Member Functions | Private Member Functions | Private Attributes

osgEarth::ImageLayer Class Reference

Inheritance diagram for osgEarth::ImageLayer:
Collaboration diagram for osgEarth::ImageLayer:

List of all members.

Public Member Functions

 ImageLayer (const ImageLayerOptions &options)
 ImageLayer (const std::string &name, const TileSourceOptions &driverOptions)
 ImageLayer (const ImageLayerOptions &options, TileSource *tileSource)
const ImageLayerOptionsgetImageLayerOptions () const
virtual const TerrainLayerOptionsgetTerrainLayerOptions () const
void addCallback (ImageLayerCallback *cb)
void removeCallback (ImageLayerCallback *cb)
virtual void setTargetProfileHint (const Profile *profile)
void setOpacity (float opacity)
float getOpacity () const
void disableLODBlending ()
bool isLODBlendingEnabled () const
GeoImage createImage (const TileKey &key, ProgressCallback *progress=0)

Protected Member Functions

osg::Image * createImageWrapper (const TileKey &key, bool cacheInLayerProfile, ProgressCallback *progress)
virtual void initTileSource ()

Private Member Functions

void initPreCacheOp ()
virtual void fireCallback (TerrainLayerCallbackMethodPtr method)
virtual void fireCallback (ImageLayerCallbackMethodPtr method)
void init ()

Private Attributes

ImageLayerOptions _runtimeOptions
osg::ref_ptr
< TileSource::ImageOperation
_preCacheOp
ImageLayerCallbackList _callbacks

Detailed Description

A map terrain layer containing bitmap image data.

Definition at line 158 of file ImageLayer.


Constructor & Destructor Documentation

ImageLayer::ImageLayer ( const ImageLayerOptions options)

Constructs a new image layer.

Definition at line 228 of file ImageLayer.cpp.

Here is the call graph for this function:

ImageLayer::ImageLayer ( const std::string &  name,
const TileSourceOptions driverOptions 
)

Constructs a new image layer with the given name and driver options.

Definition at line 235 of file ImageLayer.cpp.

                                                                                      :
TerrainLayer   ( &_runtimeOptions ),
_runtimeOptions( ImageLayerOptions(name, driverOptions) )
{
    init();
}

Here is the call graph for this function:

ImageLayer::ImageLayer ( const ImageLayerOptions options,
TileSource tileSource 
)

Constructs a new image layer with a custom TileSource.

Definition at line 242 of file ImageLayer.cpp.

                                                                                 :
TerrainLayer   ( &_runtimeOptions, tileSource ),
_runtimeOptions( options )
{
    init();
}

Here is the call graph for this function:


Member Function Documentation

void ImageLayer::addCallback ( ImageLayerCallback cb)

Adds a property notification callback to this layer

Definition at line 256 of file ImageLayer.cpp.

{
    _callbacks.push_back( cb );
}

Here is the caller graph for this function:

GeoImage ImageLayer::createImage ( const TileKey key,
ProgressCallback progress = 0 
)

Creates a GeoImage from this MapLayer

Definition at line 338 of file ImageLayer.cpp.

{
    GeoImage result;

        //OE_NOTICE << "[osgEarth::MapLayer::createImage] " << key.str() << std::endl;
        if ( !isCacheOnly() && !getTileSource()  )
        {
                OE_WARN << LC << "Error:  MapLayer does not have a valid TileSource, cannot create image " << std::endl;
                return GeoImage::INVALID;
        }

    const Profile* layerProfile = getProfile();
    const Profile* mapProfile = key.getProfile();

    if ( !getProfile() )
        {
                OE_WARN << LC << "Could not get a valid profile for Layer \"" << getName() << "\"" << std::endl;
        return GeoImage::INVALID;
        }

        //Determine whether we should cache in the Map profile or the Layer profile.
        bool cacheInMapProfile = true;
        if (mapProfile->isEquivalentTo( layerProfile ))
        {
                OE_DEBUG << LC << "Layer \"" << getName() << "\": Map and Layer profiles are equivalent " << std::endl;
        }
        //If the map profile and layer profile are in the same SRS but with different tiling scemes and exact cropping is not required, cache in the layer profile.
    else if (mapProfile->getSRS()->isEquivalentTo( layerProfile->getSRS()) && _runtimeOptions.exactCropping() == false )
        {
                OE_DEBUG << LC << "Layer \"" << getName() << "\": Map and Layer profiles are in the same SRS and non-exact cropping is allowed, caching in layer profile." << std::endl;
                cacheInMapProfile = false;
        }

        bool cacheInLayerProfile = !cacheInMapProfile;

    //Write the cache TMS file if it hasn't been written yet.
    if (!_cacheProfile.valid() && _cache.valid() && _runtimeOptions.cacheEnabled() == true && _tileSource.valid())
    {
        _cacheProfile = cacheInMapProfile ? mapProfile : _profile.get();
        _cache->storeProperties( _cacheSpec, _cacheProfile.get(), _tileSource->getPixelsPerTile() );
    }

        if (cacheInMapProfile)
        {
                OE_DEBUG << LC << "Layer \"" << getName() << "\" caching in Map profile " << std::endl;
        }

        //If we are caching in the map profile, try to get the image immediately.
    if (cacheInMapProfile && _cache.valid() && _runtimeOptions.cacheEnabled() == true )
        {
        osg::ref_ptr<const osg::Image> cachedImage;
        if ( _cache->getImage( key, _cacheSpec, cachedImage ) )
                {
                        OE_DEBUG << LC << "Layer \"" << getName()<< "\" got tile " << key.str() << " from map cache " << std::endl;

            result = GeoImage( ImageUtils::cloneImage(cachedImage.get()), key.getExtent() );
            ImageUtils::normalizeImage( result.getImage() );
            return result;
                }
        }

        //If the key profile and the source profile exactly match, simply request the image from the source
    if ( mapProfile->isEquivalentTo( layerProfile ) )
    {
                OE_DEBUG << LC << "Key and source profiles are equivalent, requesting single tile" << std::endl;
        osg::ref_ptr<const osg::Image> image;
        osg::Image* im = createImageWrapper( key, cacheInLayerProfile, progress );
        if ( im )
        {
            result = GeoImage( im, key.getExtent() );
        }
    }

    // Otherwise, we need to process the tiles.
    else
    {
                OE_DEBUG << LC << "Key and source profiles are different, creating mosaic" << std::endl;
                GeoImage mosaic;

                // Determine the intersecting keys and create and extract an appropriate image from the tiles
                std::vector<TileKey> intersectingTiles;

        //Scale the extent if necessary
        GeoExtent ext = key.getExtent();
        if ( _runtimeOptions.edgeBufferRatio().isSet() )
        {
            double ratio = _runtimeOptions.edgeBufferRatio().get();
            ext.scale(ratio, ratio);
        }

        layerProfile->getIntersectingTiles(ext, intersectingTiles);

                if (intersectingTiles.size() > 0)
                {
                        double dst_minx, dst_miny, dst_maxx, dst_maxy;
                        key.getExtent().getBounds(dst_minx, dst_miny, dst_maxx, dst_maxy);

                        osg::ref_ptr<ImageMosaic> mi = new ImageMosaic;
                        std::vector<TileKey> missingTiles;

            bool retry = false;
                        for (unsigned int j = 0; j < intersectingTiles.size(); ++j)
                        {
                                double minX, minY, maxX, maxY;
                                intersectingTiles[j].getExtent().getBounds(minX, minY, maxX, maxY);

                                OE_DEBUG << LC << "\t Intersecting Tile " << j << ": " << minX << ", " << minY << ", " << maxX << ", " << maxY << std::endl;

                                osg::ref_ptr<osg::Image> img;
                img = createImageWrapper( intersectingTiles[j], cacheInLayerProfile, progress );

                if ( img.valid() )
                {
                    if (img->getPixelFormat() != GL_RGBA || img->getDataType() != GL_UNSIGNED_BYTE || img->getInternalTextureFormat() != GL_RGBA8 )
                                        {
                        osg::ref_ptr<osg::Image> convertedImg = ImageUtils::convertToRGBA8(img.get());
                        if (convertedImg.valid())
                        {
                            img = convertedImg;
                        }
                                        }
                                        mi->getImages().push_back(TileImage(img.get(), intersectingTiles[j]));
                                }
                                else
                                {
                    if (progress && (progress->isCanceled() || progress->needsRetry()))
                    {
                        retry = true;
                        break;
                    }
                                        missingTiles.push_back(intersectingTiles[j]);
                                }
                        }

                        //if (mi->getImages().empty() || missingTiles.size() > 0)
            if (mi->getImages().empty() || retry)
                        {
                                OE_DEBUG << LC << "Couldn't create image for ImageMosaic " << std::endl;
                return GeoImage::INVALID;
                        }
                        else if (missingTiles.size() > 0)
                        {                
                osg::ref_ptr<const osg::Image> validImage = mi->getImages()[0].getImage();
                unsigned int tileWidth = validImage->s();
                unsigned int tileHeight = validImage->t();
                unsigned int tileDepth = validImage->r();
                for (unsigned int j = 0; j < missingTiles.size(); ++j)
                {
                    // Create transparent image which size equals to the size of a valid image
                    osg::ref_ptr<osg::Image> newImage = new osg::Image;
                    newImage->allocateImage(tileWidth, tileHeight, tileDepth, validImage->getPixelFormat(), validImage->getDataType());
                    unsigned char *data = newImage->data(0,0);
                    memset(data, 0, newImage->getTotalSizeInBytes());

                    mi->getImages().push_back(TileImage(newImage.get(), missingTiles[j]));
                }
                        }

                        double rxmin, rymin, rxmax, rymax;
                        mi->getExtents( rxmin, rymin, rxmax, rymax );

                        mosaic = GeoImage(
                                mi->createImage(),
                                GeoExtent( layerProfile->getSRS(), rxmin, rymin, rxmax, rymax ) );
                }

                if ( mosaic.valid() )
        {
            // the imagery must be reprojected iff:
            //  * the SRS of the image is different from the SRS of the key;
            //  * UNLESS they are both geographic SRS's (in which case we can skip reprojection)
            bool needsReprojection =
                !mosaic.getSRS()->isEquivalentTo( key.getProfile()->getSRS()) &&
                !(mosaic.getSRS()->isGeographic() && key.getProfile()->getSRS()->isGeographic());

            bool needsLeftBorder = false;
            bool needsRightBorder = false;
            bool needsTopBorder = false;
            bool needsBottomBorder = false;

            // If we don't need to reproject the data, we had to mosaic the data, so check to see if we need to add
            // an extra, transparent pixel on the sides because the data doesn't encompass the entire map.
            if (!needsReprojection)
            {
                GeoExtent keyExtent = key.getExtent();
                // If the key is geographic and the mosaic is mercator, we need to get the mercator
                // extents to determine if we need to add the border or not
                // (TODO: this might be OBE due to the elimination of the Mercator fast-path -gw)
                if (key.getExtent().getSRS()->isGeographic() && mosaic.getSRS()->isMercator())
                {
                    keyExtent = osgEarth::Registry::instance()->getGlobalMercatorProfile()->clampAndTransformExtent( 
                        key.getExtent( ));
                }


                //Use an epsilon to only add the border if it is significant enough.
                double eps = 1e-6;
                
                double leftDiff = mosaic.getExtent().xMin() - keyExtent.xMin();
                if (leftDiff > eps)
                {
                    needsLeftBorder = true;
                }

                double rightDiff = keyExtent.xMax() - mosaic.getExtent().xMax();
                if (rightDiff > eps)
                {
                    needsRightBorder = true;
                }

                double bottomDiff = mosaic.getExtent().yMin() - keyExtent.yMin();
                if (bottomDiff > eps)
                {
                    needsBottomBorder = true;
                }

                double topDiff = keyExtent.yMax() - mosaic.getExtent().yMax();
                if (topDiff > eps)
                {
                    needsTopBorder = true;
                }
            }

            if ( needsReprojection )
            {
                                OE_DEBUG << LC << "  Reprojecting image" << std::endl;

                // We actually need to reproject the image.  Note: GeoImage::reproject() will automatically
                // crop the image to the correct extents, so there is no need to crop after reprojection.
                result = mosaic.reproject( 
                    key.getProfile()->getSRS(),
                    &key.getExtent(), 
                    _runtimeOptions.reprojectedTileSize().value(), _runtimeOptions.reprojectedTileSize().value() );
            }
            else
            {
                                OE_DEBUG << LC << "  Cropping image" << std::endl;
                // crop to fit the map key extents
                GeoExtent clampedMapExt = layerProfile->clampAndTransformExtent( key.getExtent() );
                if ( clampedMapExt.isValid() )
                                {
                    int size = _runtimeOptions.exactCropping() == true ? _runtimeOptions.reprojectedTileSize().value() : 0;
                    result = mosaic.crop(clampedMapExt, _runtimeOptions.exactCropping().value(), size, size);
                                }
                else
                    result = GeoImage::INVALID;
            }

            //Add the transparent pixel AFTER the crop so that it doesn't get cropped out
            if (result.valid() && (needsLeftBorder || needsRightBorder || needsBottomBorder || needsTopBorder))
            {
                result = result.addTransparentBorder(needsLeftBorder, needsRightBorder, needsBottomBorder, needsTopBorder);
            }
        }
    }

    // Normalize the image if necessary
    if ( result.valid() )
    {
        ImageUtils::normalizeImage( result.getImage() );
    }

        //If we got a result, the cache is valid and we are caching in the map profile, write to the map cache.
    if (result.valid() && _cache.valid() && _runtimeOptions.cacheEnabled() == true && cacheInMapProfile)
        {
                OE_DEBUG << LC << "Layer \"" << getName() << "\" writing tile " << key.str() << " to cache " << std::endl;
                _cache->setImage( key, _cacheSpec, result.getImage());
        }
    return result;
}

Here is the call graph for this function:

Here is the caller graph for this function:

osg::Image * ImageLayer::createImageWrapper ( const TileKey key,
bool  cacheInLayerProfile,
ProgressCallback progress 
) [protected]

Definition at line 610 of file ImageLayer.cpp.

{
    // Results:
    //
    // * return NULL to indicate that the key exceeds the maximum LOD of the source data,
    //   and that the engine may need to generate a "fallback" tile if necessary.
    //
    // * return an "empty image" if the LOD is valid BUT the key does not intersect the
    //   source's data extents.

    osg::Image* result = 0L;

    // first check the cache.
    // TODO: find a way to avoid caching/checking when the LOD falls
    if (_cache.valid() && cacheInLayerProfile && _runtimeOptions.cacheEnabled() == true )
    {
        osg::ref_ptr<const osg::Image> cachedImage;
                if ( _cache->getImage( key, _cacheSpec, cachedImage ) )
            {
            OE_INFO << LC << " Layer \"" << getName() << "\" got " << key.str() << " from cache " << std::endl;
            return ImageUtils::cloneImage(cachedImage.get());
        }
    }

        if ( !isCacheOnly() )
        {
        TileSource* source = getTileSource();
        if ( !source )
            return 0L;

        // Only try to get the image if it's not in the blacklist
        if ( !source->getBlacklist()->contains(key.getTileId()) )
        {
            // if the tile source cannot service this key's LOD, return NULL.
            if ( source->hasDataAtLOD( key.getLevelOfDetail() ) )
            {
                // if the key's extent intersects the source's extent, ask the
                // source for an image.
                if ( source->hasDataInExtent( key.getExtent() ) )
                {
                    //Take a reference to the preCacheOp, there is a potential for it to be
                    //overwritten and deleted if this ImageLayer is added to another Map
                    //while createImage is going on.
                    osg::ref_ptr< TileSource::ImageOperation > op = _preCacheOp;
                    result = source->createImage( key, op.get(), progress );

                    // if no result was created, add this key to the blacklist.
                    if ( result == 0L && (!progress || !progress->isCanceled()) )
                    {
                        //Add the tile to the blacklist
                        source->getBlacklist()->add(key.getTileId());
                    }
                }

                // otherwise, generate an empty image.
                else
                {
                    result = ImageUtils::createEmptyImage();
                }
            }

            else
            {
                // in this case, the source cannot service the LOD
                result = NULL;
            }            
        }

        // Cache is necessary:
        if ( result && _cache.valid() && cacheInLayerProfile && _runtimeOptions.cacheEnabled() == true )
                {
                        _cache->setImage( key, _cacheSpec, result );
                }
        }

    return result;
}

Here is the call graph for this function:

Here is the caller graph for this function:

void ImageLayer::disableLODBlending ( )

Definition at line 297 of file ImageLayer.cpp.

Here is the call graph for this function:

void ImageLayer::fireCallback ( TerrainLayerCallbackMethodPtr  method) [private, virtual]

Implements osgEarth::TerrainLayer.

Definition at line 270 of file ImageLayer.cpp.

{
    for( ImageLayerCallbackList::const_iterator i = _callbacks.begin(); i != _callbacks.end(); ++i )
    {
        ImageLayerCallback* cb = i->get();
        (cb->*method)( this );
    }
}

Here is the caller graph for this function:

void ImageLayer::fireCallback ( ImageLayerCallbackMethodPtr  method) [private, virtual]

Definition at line 280 of file ImageLayer.cpp.

{
    for( ImageLayerCallbackList::const_iterator i = _callbacks.begin(); i != _callbacks.end(); ++i )
    {
        ImageLayerCallback* cb = i->get();
        (cb->*method)( this );
    }
}
const ImageLayerOptions& osgEarth::ImageLayer::getImageLayerOptions ( ) const [inline]

Access to the initialization options used to create this image layer

Definition at line 180 of file ImageLayer.

{ return _runtimeOptions; }

Here is the caller graph for this function:

float osgEarth::ImageLayer::getOpacity ( ) const [inline]

Definition at line 195 of file ImageLayer.

{ return *_runtimeOptions.opacity(); }

Here is the caller graph for this function:

virtual const TerrainLayerOptions& osgEarth::ImageLayer::getTerrainLayerOptions ( ) const [inline, virtual]

The options data connected to this layer.

Reimplemented from osgEarth::TerrainLayer.

Definition at line 181 of file ImageLayer.

{ return _runtimeOptions; }
void ImageLayer::init ( ) [private]

Reimplemented from osgEarth::TerrainLayer.

Definition at line 250 of file ImageLayer.cpp.

{
    //nop
}

Here is the caller graph for this function:

void ImageLayer::initPreCacheOp ( ) [private]

Definition at line 323 of file ImageLayer.cpp.

{
    bool layerInTargetProfile = 
        _targetProfileHint.valid() &&
        getProfile() &&
        _targetProfileHint->isEquivalentTo( getProfile() );

    ImageLayerPreCacheOperation* op = new ImageLayerPreCacheOperation();    
    op->_processor.init( _runtimeOptions, layerInTargetProfile );

    _preCacheOp = op;

}

Here is the call graph for this function:

Here is the caller graph for this function:

void ImageLayer::initTileSource ( ) [protected, virtual]

Reimplemented from osgEarth::TerrainLayer.

Definition at line 313 of file ImageLayer.cpp.

{
    // call superclass first.
    TerrainLayer::initTileSource();

    // install the pre-caching image processor operation.
    initPreCacheOp();
}

Here is the call graph for this function:

bool osgEarth::ImageLayer::isLODBlendingEnabled ( ) const [inline]

Definition at line 198 of file ImageLayer.

{ return *_runtimeOptions.lodBlending(); }
void ImageLayer::removeCallback ( ImageLayerCallback cb)

Removes a property notification callback from this layer

Definition at line 262 of file ImageLayer.cpp.

{
    ImageLayerCallbackList::iterator i = std::find( _callbacks.begin(), _callbacks.end(), cb );
    if ( i != _callbacks.end() ) 
        _callbacks.erase( i );
}

Here is the caller graph for this function:

void ImageLayer::setOpacity ( float  opacity)

Definition at line 290 of file ImageLayer.cpp.

{
    _runtimeOptions.opacity() = osg::clampBetween( value, 0.0f, 1.0f );
    fireCallback( &ImageLayerCallback::onOpacityChanged );
}

Here is the call graph for this function:

void ImageLayer::setTargetProfileHint ( const Profile profile) [virtual]

Override: see TerrainLayer

Reimplemented from osgEarth::TerrainLayer.

Definition at line 303 of file ImageLayer.cpp.

{
    TerrainLayer::setTargetProfileHint( profile );

    // if we've already constructed the pre-cache operation, reinitialize it.
    if ( _preCacheOp.valid() )
        initPreCacheOp();
}

Here is the call graph for this function:

Here is the caller graph for this function:


Member Data Documentation

Definition at line 226 of file ImageLayer.

Definition at line 222 of file ImageLayer.

Reimplemented from osgEarth::TerrainLayer.

Definition at line 217 of file ImageLayer.


The documentation for this class was generated from the following files:
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines