|
osgEarth 2.1.1
|
Inheritance diagram for osgEarth::ImageLayer:
Collaboration diagram for osgEarth::ImageLayer:A map terrain layer containing bitmap image data.
Definition at line 158 of file ImageLayer.
| ImageLayer::ImageLayer | ( | const ImageLayerOptions & | options | ) |
Constructs a new image layer.
Definition at line 228 of file ImageLayer.cpp.
: TerrainLayer( &_runtimeOptions ), _runtimeOptions( options ) { init(); }
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:| 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.
{
_runtimeOptions.lodBlending() = false;
}
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:Definition at line 226 of file ImageLayer.
osg::ref_ptr<TileSource::ImageOperation> osgEarth::ImageLayer::_preCacheOp [private] |
Definition at line 222 of file ImageLayer.
Reimplemented from osgEarth::TerrainLayer.
Definition at line 217 of file ImageLayer.
1.7.3