osgEarth 2.1.1

/home/cube/sources/osgearth/src/osgEarth/ImageLayer.cpp

Go to the documentation of this file.
00001 /* -*-c++-*- */
00002 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
00003  * Copyright 2008-2010 Pelican Mapping
00004  * http://osgearth.org
00005  *
00006  * osgEarth is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU Lesser General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU Lesser General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU Lesser General Public License
00017  * along with this program.  If not, see <http://www.gnu.org/licenses/>
00018  */
00019 #include <osgEarth/ImageLayer>
00020 #include <osgEarth/TileSource>
00021 #include <osgEarth/ImageMosaic>
00022 #include <osgEarth/ImageUtils>
00023 #include <osgEarth/Registry>
00024 #include <osgEarth/StringUtils>
00025 #include <osg/Version>
00026 #include <memory.h>
00027 #include <limits.h>
00028 
00029 using namespace osgEarth;
00030 using namespace OpenThreads;
00031 
00032 #define LC "[ImageLayer] "
00033 
00034 //------------------------------------------------------------------------
00035 
00036 ImageLayerOptions::ImageLayerOptions( const ConfigOptions& options ) :
00037 TerrainLayerOptions(options)
00038 {
00039     setDefaults();
00040     fromConfig( _conf );
00041 }
00042 
00043 ImageLayerOptions::ImageLayerOptions( const std::string& name, const TileSourceOptions& driverOpt ) :
00044 TerrainLayerOptions(name, driverOpt)
00045 {
00046     setDefaults();
00047     fromConfig( _conf );
00048 }
00049 
00050 void
00051 ImageLayerOptions::setDefaults()
00052 {
00053     _opacity.init( 1.0f );
00054     _transparentColor.init( osg::Vec4ub(0,0,0,0) );
00055     _minRange.init( -FLT_MAX );
00056     _maxRange.init( FLT_MAX );
00057     _lodBlending.init( false );
00058 }
00059 
00060 void
00061 ImageLayerOptions::mergeConfig( const Config& conf )
00062 {
00063     TerrainLayerOptions::mergeConfig( conf );
00064     fromConfig( conf );
00065 }
00066 
00067 void
00068 ImageLayerOptions::fromConfig( const Config& conf )
00069 {
00070     conf.getIfSet( "nodata_image", _noDataImageFilename );
00071     conf.getIfSet( "opacity", _opacity );
00072     conf.getIfSet( "min_range", _minRange );
00073     conf.getIfSet( "max_range", _maxRange );
00074     conf.getIfSet( "lod_blending", _lodBlending );
00075 
00076     if ( conf.hasValue( "transparent_color" ) )
00077         _transparentColor = stringToColor( conf.value( "transparent_color" ), osg::Vec4ub(0,0,0,0));
00078 
00079         //Load the filter settings
00080         conf.getIfSet("mag_filter","LINEAR",                _magFilter,osg::Texture::LINEAR);
00081     conf.getIfSet("mag_filter","LINEAR_MIPMAP_LINEAR",  _magFilter,osg::Texture::LINEAR_MIPMAP_LINEAR);
00082     conf.getIfSet("mag_filter","LINEAR_MIPMAP_NEAREST", _magFilter,osg::Texture::LINEAR_MIPMAP_NEAREST);
00083     conf.getIfSet("mag_filter","NEAREST",               _magFilter,osg::Texture::NEAREST);
00084     conf.getIfSet("mag_filter","NEAREST_MIPMAP_LINEAR", _magFilter,osg::Texture::NEAREST_MIPMAP_LINEAR);
00085     conf.getIfSet("mag_filter","NEAREST_MIPMAP_NEAREST",_magFilter,osg::Texture::NEAREST_MIPMAP_NEAREST);
00086     conf.getIfSet("min_filter","LINEAR",                _minFilter,osg::Texture::LINEAR);
00087     conf.getIfSet("min_filter","LINEAR_MIPMAP_LINEAR",  _minFilter,osg::Texture::LINEAR_MIPMAP_LINEAR);
00088     conf.getIfSet("min_filter","LINEAR_MIPMAP_NEAREST", _minFilter,osg::Texture::LINEAR_MIPMAP_NEAREST);
00089     conf.getIfSet("min_filter","NEAREST",               _minFilter,osg::Texture::NEAREST);
00090     conf.getIfSet("min_filter","NEAREST_MIPMAP_LINEAR", _minFilter,osg::Texture::NEAREST_MIPMAP_LINEAR);
00091     conf.getIfSet("min_filter","NEAREST_MIPMAP_NEAREST",_minFilter,osg::Texture::NEAREST_MIPMAP_NEAREST);   
00092 }
00093 
00094 Config
00095 ImageLayerOptions::getConfig() const
00096 {
00097     Config conf = TerrainLayerOptions::getConfig();
00098     conf.updateIfSet( "nodata_image", _noDataImageFilename );
00099     conf.updateIfSet( "opacity", _opacity );
00100     conf.updateIfSet( "min_range", _minRange );
00101     conf.updateIfSet( "max_range", _maxRange );
00102     conf.updateIfSet( "lod_blending", _lodBlending );
00103 
00104         if (_transparentColor.isSet())
00105         conf.update("transparent_color", colorToString( _transparentColor.value()));
00106 
00107     //Save the filter settings
00108         conf.updateIfSet("mag_filter","LINEAR",                _magFilter,osg::Texture::LINEAR);
00109     conf.updateIfSet("mag_filter","LINEAR_MIPMAP_LINEAR",  _magFilter,osg::Texture::LINEAR_MIPMAP_LINEAR);
00110     conf.updateIfSet("mag_filter","LINEAR_MIPMAP_NEAREST", _magFilter,osg::Texture::LINEAR_MIPMAP_NEAREST);
00111     conf.updateIfSet("mag_filter","NEAREST",               _magFilter,osg::Texture::NEAREST);
00112     conf.updateIfSet("mag_filter","NEAREST_MIPMAP_LINEAR", _magFilter,osg::Texture::NEAREST_MIPMAP_LINEAR);
00113     conf.updateIfSet("mag_filter","NEAREST_MIPMAP_NEAREST",_magFilter,osg::Texture::NEAREST_MIPMAP_NEAREST);
00114     conf.updateIfSet("min_filter","LINEAR",                _minFilter,osg::Texture::LINEAR);
00115     conf.updateIfSet("min_filter","LINEAR_MIPMAP_LINEAR",  _minFilter,osg::Texture::LINEAR_MIPMAP_LINEAR);
00116     conf.updateIfSet("min_filter","LINEAR_MIPMAP_NEAREST", _minFilter,osg::Texture::LINEAR_MIPMAP_NEAREST);
00117     conf.updateIfSet("min_filter","NEAREST",               _minFilter,osg::Texture::NEAREST);
00118     conf.updateIfSet("min_filter","NEAREST_MIPMAP_LINEAR", _minFilter,osg::Texture::NEAREST_MIPMAP_LINEAR);
00119     conf.updateIfSet("min_filter","NEAREST_MIPMAP_NEAREST",_minFilter,osg::Texture::NEAREST_MIPMAP_NEAREST);
00120     
00121     return conf;
00122 }
00123 
00124 //------------------------------------------------------------------------
00125 
00126 namespace
00127 {
00128     struct ImageLayerPreCacheOperation : public TileSource::ImageOperation
00129     {
00130         void operator()( osg::ref_ptr<osg::Image>& image )
00131         {
00132             _processor.process( image );
00133         }
00134 
00135         ImageLayerTileProcessor _processor;
00136     };
00137     
00138     struct ApplyChromaKey
00139     {
00140         osg::Vec4f _chromaKey;
00141         bool operator()( osg::Vec4f& pixel ) {
00142             bool equiv = ImageUtils::areRGBEquivalent( pixel, _chromaKey );
00143             if ( equiv ) pixel.a() = 0.0f;
00144             return equiv;
00145         }
00146     };
00147 }
00148 
00149 //------------------------------------------------------------------------
00150 
00151 ImageLayerTileProcessor::ImageLayerTileProcessor( const ImageLayerOptions& options )
00152 {
00153     init( options, false );
00154 }
00155 
00156 void
00157 ImageLayerTileProcessor::init( const ImageLayerOptions& options, bool layerInTargetProfile )
00158 {
00159     _options = options;
00160     _layerInTargetProfile = layerInTargetProfile;
00161 
00162     if ( _layerInTargetProfile )
00163         OE_DEBUG << LC << "Good, the layer and map have the same profile." << std::endl;
00164 
00165     const osg::Vec4ub& ck= *_options.transparentColor();
00166     _chromaKey.set( ck.r() / 255.0f, ck.g() / 255.0f, ck.b() / 255.0f, 1.0 );
00167 
00168     if ( _options.noDataImageFilename().isSet() && !_options.noDataImageFilename()->empty() )
00169     {
00170         _noDataImage = URI(*_options.noDataImageFilename()).readImage();
00171         if ( !_noDataImage.valid() )
00172         {
00173             OE_WARN << "Warning: Could not read nodata image from \"" << _options.noDataImageFilename().value() << "\"" << std::endl;
00174         }
00175     }
00176 }
00177 
00178 void
00179 ImageLayerTileProcessor::process( osg::ref_ptr<osg::Image>& image ) const
00180 {
00181     if ( !image.valid() )
00182         return;
00183 
00184     // Check to see if the image is the nodata image
00185     if ( _noDataImage.valid() )
00186     {
00187         if (ImageUtils::areEquivalent(image.get(), _noDataImage.get()))
00188         {
00189             //OE_DEBUG << LC << "Found nodata" << std::endl;
00190             image = 0L;
00191             return;
00192         }
00193     }
00194 
00195     // If this is a compressed image, uncompress it IF the image is not already in the
00196     // target profile...becuase if it's not in the target profile, we will have to do
00197     // some mosaicing...and we can't mosaic a compressed image.
00198     if (!_layerInTargetProfile &&
00199         ImageUtils::isCompressed(image.get()) &&
00200         ImageUtils::canConvert(image.get(), GL_RGBA, GL_UNSIGNED_BYTE) )
00201     {
00202         image = ImageUtils::convertToRGBA8( image.get() );
00203     }
00204 
00205     // Apply a transparent color mask if one is specified
00206     if ( _options.transparentColor().isSet() )
00207     {
00208         if ( !ImageUtils::hasAlphaChannel(image.get()) && ImageUtils::canConvert(image.get(), GL_RGBA, GL_UNSIGNED_BYTE) )
00209         {
00210             // if the image doesn't have an alpha channel, we must convert it to
00211             // a format that does before continuing.
00212             image = ImageUtils::convertToRGBA8( image.get() );
00213         }           
00214 
00215         ImageUtils::PixelVisitor<ApplyChromaKey> applyChroma;
00216         applyChroma._chromaKey = _chromaKey;
00217         applyChroma.accept( image.get() );
00218     }
00219 
00220     // protected against multi threaded access. This is a requirement in sequential/preemptive mode, 
00221     // for example. This used to be in TextureCompositorTexArray::prepareImage.
00222     // TODO: review whether this affects performance.    
00223     image->setDataVariance( osg::Object::DYNAMIC );
00224 }
00225 
00226 //------------------------------------------------------------------------
00227 
00228 ImageLayer::ImageLayer( const ImageLayerOptions& options ) :
00229 TerrainLayer( &_runtimeOptions ),
00230 _runtimeOptions( options )
00231 {
00232     init();
00233 }
00234 
00235 ImageLayer::ImageLayer( const std::string& name, const TileSourceOptions& driverOptions ) :
00236 TerrainLayer   ( &_runtimeOptions ),
00237 _runtimeOptions( ImageLayerOptions(name, driverOptions) )
00238 {
00239     init();
00240 }
00241 
00242 ImageLayer::ImageLayer( const ImageLayerOptions& options, TileSource* tileSource ) :
00243 TerrainLayer   ( &_runtimeOptions, tileSource ),
00244 _runtimeOptions( options )
00245 {
00246     init();
00247 }
00248 
00249 void
00250 ImageLayer::init()
00251 {
00252     //nop
00253 }
00254 
00255 void
00256 ImageLayer::addCallback( ImageLayerCallback* cb )
00257 {
00258     _callbacks.push_back( cb );
00259 }
00260 
00261 void
00262 ImageLayer::removeCallback( ImageLayerCallback* cb )
00263 {
00264     ImageLayerCallbackList::iterator i = std::find( _callbacks.begin(), _callbacks.end(), cb );
00265     if ( i != _callbacks.end() ) 
00266         _callbacks.erase( i );
00267 }
00268 
00269 void
00270 ImageLayer::fireCallback( TerrainLayerCallbackMethodPtr method )
00271 {
00272     for( ImageLayerCallbackList::const_iterator i = _callbacks.begin(); i != _callbacks.end(); ++i )
00273     {
00274         ImageLayerCallback* cb = i->get();
00275         (cb->*method)( this );
00276     }
00277 }
00278 
00279 void
00280 ImageLayer::fireCallback( ImageLayerCallbackMethodPtr method )
00281 {
00282     for( ImageLayerCallbackList::const_iterator i = _callbacks.begin(); i != _callbacks.end(); ++i )
00283     {
00284         ImageLayerCallback* cb = i->get();
00285         (cb->*method)( this );
00286     }
00287 }
00288 
00289 void
00290 ImageLayer::setOpacity( float value ) 
00291 {
00292     _runtimeOptions.opacity() = osg::clampBetween( value, 0.0f, 1.0f );
00293     fireCallback( &ImageLayerCallback::onOpacityChanged );
00294 }
00295 
00296 void 
00297 ImageLayer::disableLODBlending()
00298 {
00299     _runtimeOptions.lodBlending() = false;
00300 }
00301 
00302 void
00303 ImageLayer::setTargetProfileHint( const Profile* profile )
00304 {
00305     TerrainLayer::setTargetProfileHint( profile );
00306 
00307     // if we've already constructed the pre-cache operation, reinitialize it.
00308     if ( _preCacheOp.valid() )
00309         initPreCacheOp();
00310 }
00311 
00312 void
00313 ImageLayer::initTileSource()
00314 {
00315     // call superclass first.
00316     TerrainLayer::initTileSource();
00317 
00318     // install the pre-caching image processor operation.
00319     initPreCacheOp();
00320 }
00321 
00322 void
00323 ImageLayer::initPreCacheOp()
00324 {
00325     bool layerInTargetProfile = 
00326         _targetProfileHint.valid() &&
00327         getProfile() &&
00328         _targetProfileHint->isEquivalentTo( getProfile() );
00329 
00330     ImageLayerPreCacheOperation* op = new ImageLayerPreCacheOperation();    
00331     op->_processor.init( _runtimeOptions, layerInTargetProfile );
00332 
00333     _preCacheOp = op;
00334 
00335 }
00336 
00337 GeoImage
00338 ImageLayer::createImage( const TileKey& key, ProgressCallback* progress)
00339 {
00340     GeoImage result;
00341 
00342         //OE_NOTICE << "[osgEarth::MapLayer::createImage] " << key.str() << std::endl;
00343         if ( !isCacheOnly() && !getTileSource()  )
00344         {
00345                 OE_WARN << LC << "Error:  MapLayer does not have a valid TileSource, cannot create image " << std::endl;
00346                 return GeoImage::INVALID;
00347         }
00348 
00349     const Profile* layerProfile = getProfile();
00350     const Profile* mapProfile = key.getProfile();
00351 
00352     if ( !getProfile() )
00353         {
00354                 OE_WARN << LC << "Could not get a valid profile for Layer \"" << getName() << "\"" << std::endl;
00355         return GeoImage::INVALID;
00356         }
00357 
00358         //Determine whether we should cache in the Map profile or the Layer profile.
00359         bool cacheInMapProfile = true;
00360         if (mapProfile->isEquivalentTo( layerProfile ))
00361         {
00362                 OE_DEBUG << LC << "Layer \"" << getName() << "\": Map and Layer profiles are equivalent " << std::endl;
00363         }
00364         //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.
00365     else if (mapProfile->getSRS()->isEquivalentTo( layerProfile->getSRS()) && _runtimeOptions.exactCropping() == false )
00366         {
00367                 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;
00368                 cacheInMapProfile = false;
00369         }
00370 
00371         bool cacheInLayerProfile = !cacheInMapProfile;
00372 
00373     //Write the cache TMS file if it hasn't been written yet.
00374     if (!_cacheProfile.valid() && _cache.valid() && _runtimeOptions.cacheEnabled() == true && _tileSource.valid())
00375     {
00376         _cacheProfile = cacheInMapProfile ? mapProfile : _profile.get();
00377         _cache->storeProperties( _cacheSpec, _cacheProfile.get(), _tileSource->getPixelsPerTile() );
00378     }
00379 
00380         if (cacheInMapProfile)
00381         {
00382                 OE_DEBUG << LC << "Layer \"" << getName() << "\" caching in Map profile " << std::endl;
00383         }
00384 
00385         //If we are caching in the map profile, try to get the image immediately.
00386     if (cacheInMapProfile && _cache.valid() && _runtimeOptions.cacheEnabled() == true )
00387         {
00388         osg::ref_ptr<const osg::Image> cachedImage;
00389         if ( _cache->getImage( key, _cacheSpec, cachedImage ) )
00390                 {
00391                         OE_DEBUG << LC << "Layer \"" << getName()<< "\" got tile " << key.str() << " from map cache " << std::endl;
00392 
00393             result = GeoImage( ImageUtils::cloneImage(cachedImage.get()), key.getExtent() );
00394             ImageUtils::normalizeImage( result.getImage() );
00395             return result;
00396                 }
00397         }
00398 
00399         //If the key profile and the source profile exactly match, simply request the image from the source
00400     if ( mapProfile->isEquivalentTo( layerProfile ) )
00401     {
00402                 OE_DEBUG << LC << "Key and source profiles are equivalent, requesting single tile" << std::endl;
00403         osg::ref_ptr<const osg::Image> image;
00404         osg::Image* im = createImageWrapper( key, cacheInLayerProfile, progress );
00405         if ( im )
00406         {
00407             result = GeoImage( im, key.getExtent() );
00408         }
00409     }
00410 
00411     // Otherwise, we need to process the tiles.
00412     else
00413     {
00414                 OE_DEBUG << LC << "Key and source profiles are different, creating mosaic" << std::endl;
00415                 GeoImage mosaic;
00416 
00417                 // Determine the intersecting keys and create and extract an appropriate image from the tiles
00418                 std::vector<TileKey> intersectingTiles;
00419 
00420         //Scale the extent if necessary
00421         GeoExtent ext = key.getExtent();
00422         if ( _runtimeOptions.edgeBufferRatio().isSet() )
00423         {
00424             double ratio = _runtimeOptions.edgeBufferRatio().get();
00425             ext.scale(ratio, ratio);
00426         }
00427 
00428         layerProfile->getIntersectingTiles(ext, intersectingTiles);
00429 
00430                 if (intersectingTiles.size() > 0)
00431                 {
00432                         double dst_minx, dst_miny, dst_maxx, dst_maxy;
00433                         key.getExtent().getBounds(dst_minx, dst_miny, dst_maxx, dst_maxy);
00434 
00435                         osg::ref_ptr<ImageMosaic> mi = new ImageMosaic;
00436                         std::vector<TileKey> missingTiles;
00437 
00438             bool retry = false;
00439                         for (unsigned int j = 0; j < intersectingTiles.size(); ++j)
00440                         {
00441                                 double minX, minY, maxX, maxY;
00442                                 intersectingTiles[j].getExtent().getBounds(minX, minY, maxX, maxY);
00443 
00444                                 OE_DEBUG << LC << "\t Intersecting Tile " << j << ": " << minX << ", " << minY << ", " << maxX << ", " << maxY << std::endl;
00445 
00446                                 osg::ref_ptr<osg::Image> img;
00447                 img = createImageWrapper( intersectingTiles[j], cacheInLayerProfile, progress );
00448 
00449                 if ( img.valid() )
00450                 {
00451                     if (img->getPixelFormat() != GL_RGBA || img->getDataType() != GL_UNSIGNED_BYTE || img->getInternalTextureFormat() != GL_RGBA8 )
00452                                         {
00453                         osg::ref_ptr<osg::Image> convertedImg = ImageUtils::convertToRGBA8(img.get());
00454                         if (convertedImg.valid())
00455                         {
00456                             img = convertedImg;
00457                         }
00458                                         }
00459                                         mi->getImages().push_back(TileImage(img.get(), intersectingTiles[j]));
00460                                 }
00461                                 else
00462                                 {
00463                     if (progress && (progress->isCanceled() || progress->needsRetry()))
00464                     {
00465                         retry = true;
00466                         break;
00467                     }
00468                                         missingTiles.push_back(intersectingTiles[j]);
00469                                 }
00470                         }
00471 
00472                         //if (mi->getImages().empty() || missingTiles.size() > 0)
00473             if (mi->getImages().empty() || retry)
00474                         {
00475                                 OE_DEBUG << LC << "Couldn't create image for ImageMosaic " << std::endl;
00476                 return GeoImage::INVALID;
00477                         }
00478                         else if (missingTiles.size() > 0)
00479                         {                
00480                 osg::ref_ptr<const osg::Image> validImage = mi->getImages()[0].getImage();
00481                 unsigned int tileWidth = validImage->s();
00482                 unsigned int tileHeight = validImage->t();
00483                 unsigned int tileDepth = validImage->r();
00484                 for (unsigned int j = 0; j < missingTiles.size(); ++j)
00485                 {
00486                     // Create transparent image which size equals to the size of a valid image
00487                     osg::ref_ptr<osg::Image> newImage = new osg::Image;
00488                     newImage->allocateImage(tileWidth, tileHeight, tileDepth, validImage->getPixelFormat(), validImage->getDataType());
00489                     unsigned char *data = newImage->data(0,0);
00490                     memset(data, 0, newImage->getTotalSizeInBytes());
00491 
00492                     mi->getImages().push_back(TileImage(newImage.get(), missingTiles[j]));
00493                 }
00494                         }
00495 
00496                         double rxmin, rymin, rxmax, rymax;
00497                         mi->getExtents( rxmin, rymin, rxmax, rymax );
00498 
00499                         mosaic = GeoImage(
00500                                 mi->createImage(),
00501                                 GeoExtent( layerProfile->getSRS(), rxmin, rymin, rxmax, rymax ) );
00502                 }
00503 
00504                 if ( mosaic.valid() )
00505         {
00506             // the imagery must be reprojected iff:
00507             //  * the SRS of the image is different from the SRS of the key;
00508             //  * UNLESS they are both geographic SRS's (in which case we can skip reprojection)
00509             bool needsReprojection =
00510                 !mosaic.getSRS()->isEquivalentTo( key.getProfile()->getSRS()) &&
00511                 !(mosaic.getSRS()->isGeographic() && key.getProfile()->getSRS()->isGeographic());
00512 
00513             bool needsLeftBorder = false;
00514             bool needsRightBorder = false;
00515             bool needsTopBorder = false;
00516             bool needsBottomBorder = false;
00517 
00518             // If we don't need to reproject the data, we had to mosaic the data, so check to see if we need to add
00519             // an extra, transparent pixel on the sides because the data doesn't encompass the entire map.
00520             if (!needsReprojection)
00521             {
00522                 GeoExtent keyExtent = key.getExtent();
00523                 // If the key is geographic and the mosaic is mercator, we need to get the mercator
00524                 // extents to determine if we need to add the border or not
00525                 // (TODO: this might be OBE due to the elimination of the Mercator fast-path -gw)
00526                 if (key.getExtent().getSRS()->isGeographic() && mosaic.getSRS()->isMercator())
00527                 {
00528                     keyExtent = osgEarth::Registry::instance()->getGlobalMercatorProfile()->clampAndTransformExtent( 
00529                         key.getExtent( ));
00530                 }
00531 
00532 
00533                 //Use an epsilon to only add the border if it is significant enough.
00534                 double eps = 1e-6;
00535                 
00536                 double leftDiff = mosaic.getExtent().xMin() - keyExtent.xMin();
00537                 if (leftDiff > eps)
00538                 {
00539                     needsLeftBorder = true;
00540                 }
00541 
00542                 double rightDiff = keyExtent.xMax() - mosaic.getExtent().xMax();
00543                 if (rightDiff > eps)
00544                 {
00545                     needsRightBorder = true;
00546                 }
00547 
00548                 double bottomDiff = mosaic.getExtent().yMin() - keyExtent.yMin();
00549                 if (bottomDiff > eps)
00550                 {
00551                     needsBottomBorder = true;
00552                 }
00553 
00554                 double topDiff = keyExtent.yMax() - mosaic.getExtent().yMax();
00555                 if (topDiff > eps)
00556                 {
00557                     needsTopBorder = true;
00558                 }
00559             }
00560 
00561             if ( needsReprojection )
00562             {
00563                                 OE_DEBUG << LC << "  Reprojecting image" << std::endl;
00564 
00565                 // We actually need to reproject the image.  Note: GeoImage::reproject() will automatically
00566                 // crop the image to the correct extents, so there is no need to crop after reprojection.
00567                 result = mosaic.reproject( 
00568                     key.getProfile()->getSRS(),
00569                     &key.getExtent(), 
00570                     _runtimeOptions.reprojectedTileSize().value(), _runtimeOptions.reprojectedTileSize().value() );
00571             }
00572             else
00573             {
00574                                 OE_DEBUG << LC << "  Cropping image" << std::endl;
00575                 // crop to fit the map key extents
00576                 GeoExtent clampedMapExt = layerProfile->clampAndTransformExtent( key.getExtent() );
00577                 if ( clampedMapExt.isValid() )
00578                                 {
00579                     int size = _runtimeOptions.exactCropping() == true ? _runtimeOptions.reprojectedTileSize().value() : 0;
00580                     result = mosaic.crop(clampedMapExt, _runtimeOptions.exactCropping().value(), size, size);
00581                                 }
00582                 else
00583                     result = GeoImage::INVALID;
00584             }
00585 
00586             //Add the transparent pixel AFTER the crop so that it doesn't get cropped out
00587             if (result.valid() && (needsLeftBorder || needsRightBorder || needsBottomBorder || needsTopBorder))
00588             {
00589                 result = result.addTransparentBorder(needsLeftBorder, needsRightBorder, needsBottomBorder, needsTopBorder);
00590             }
00591         }
00592     }
00593 
00594     // Normalize the image if necessary
00595     if ( result.valid() )
00596     {
00597         ImageUtils::normalizeImage( result.getImage() );
00598     }
00599 
00600         //If we got a result, the cache is valid and we are caching in the map profile, write to the map cache.
00601     if (result.valid() && _cache.valid() && _runtimeOptions.cacheEnabled() == true && cacheInMapProfile)
00602         {
00603                 OE_DEBUG << LC << "Layer \"" << getName() << "\" writing tile " << key.str() << " to cache " << std::endl;
00604                 _cache->setImage( key, _cacheSpec, result.getImage());
00605         }
00606     return result;
00607 }
00608 
00609 osg::Image*
00610 ImageLayer::createImageWrapper(const TileKey& key,
00611                                bool cacheInLayerProfile,
00612                                ProgressCallback* progress )
00613 {
00614     // Results:
00615     //
00616     // * return NULL to indicate that the key exceeds the maximum LOD of the source data,
00617     //   and that the engine may need to generate a "fallback" tile if necessary.
00618     //
00619     // * return an "empty image" if the LOD is valid BUT the key does not intersect the
00620     //   source's data extents.
00621 
00622     osg::Image* result = 0L;
00623 
00624     // first check the cache.
00625     // TODO: find a way to avoid caching/checking when the LOD falls
00626     if (_cache.valid() && cacheInLayerProfile && _runtimeOptions.cacheEnabled() == true )
00627     {
00628         osg::ref_ptr<const osg::Image> cachedImage;
00629                 if ( _cache->getImage( key, _cacheSpec, cachedImage ) )
00630             {
00631             OE_INFO << LC << " Layer \"" << getName() << "\" got " << key.str() << " from cache " << std::endl;
00632             return ImageUtils::cloneImage(cachedImage.get());
00633         }
00634     }
00635 
00636         if ( !isCacheOnly() )
00637         {
00638         TileSource* source = getTileSource();
00639         if ( !source )
00640             return 0L;
00641 
00642         // Only try to get the image if it's not in the blacklist
00643         if ( !source->getBlacklist()->contains(key.getTileId()) )
00644         {
00645             // if the tile source cannot service this key's LOD, return NULL.
00646             if ( source->hasDataAtLOD( key.getLevelOfDetail() ) )
00647             {
00648                 // if the key's extent intersects the source's extent, ask the
00649                 // source for an image.
00650                 if ( source->hasDataInExtent( key.getExtent() ) )
00651                 {
00652                     //Take a reference to the preCacheOp, there is a potential for it to be
00653                     //overwritten and deleted if this ImageLayer is added to another Map
00654                     //while createImage is going on.
00655                     osg::ref_ptr< TileSource::ImageOperation > op = _preCacheOp;
00656                     result = source->createImage( key, op.get(), progress );
00657 
00658                     // if no result was created, add this key to the blacklist.
00659                     if ( result == 0L && (!progress || !progress->isCanceled()) )
00660                     {
00661                         //Add the tile to the blacklist
00662                         source->getBlacklist()->add(key.getTileId());
00663                     }
00664                 }
00665 
00666                 // otherwise, generate an empty image.
00667                 else
00668                 {
00669                     result = ImageUtils::createEmptyImage();
00670                 }
00671             }
00672 
00673             else
00674             {
00675                 // in this case, the source cannot service the LOD
00676                 result = NULL;
00677             }            
00678         }
00679 
00680         // Cache is necessary:
00681         if ( result && _cache.valid() && cacheInLayerProfile && _runtimeOptions.cacheEnabled() == true )
00682                 {
00683                         _cache->setImage( key, _cacheSpec, result );
00684                 }
00685         }
00686 
00687     return result;
00688 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines