osgEarth 2.1.1
|
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(); }
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(); }
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(); }
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 ); }
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; }
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; }
void ImageLayer::disableLODBlending | ( | ) |
Definition at line 297 of file ImageLayer.cpp.
{ _runtimeOptions.lodBlending() = false; }
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 ); } }
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; }
float osgEarth::ImageLayer::getOpacity | ( | ) | const [inline] |
Definition at line 195 of file ImageLayer.
{ return *_runtimeOptions.opacity(); }
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
}
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; }
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(); }
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 ); }
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 ); }
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(); }
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.