osgEarth 2.1.1

/home/cube/sources/osgearth/src/osgEarth/Caching.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 <limits.h>
00020 #include <iomanip>
00021 
00022 #include <osgEarth/Caching>
00023 #include <osgEarth/ImageToHeightFieldConverter>
00024 #include <osgEarth/FileUtils>
00025 #include <osgEarth/ImageUtils>
00026 #include <osgEarth/ThreadingUtils>
00027 
00028 #include <osgDB/FileUtils>
00029 #include <osgDB/FileNameUtils>
00030 #include <osgDB/ReadFile>
00031 #include <osgDB/WriteFile>
00032 
00033 #include <OpenThreads/ScopedLock>
00034 
00035 using namespace osgEarth;
00036 
00037 #define LC "[Cache] "
00038 
00039 //------------------------------------------------------------------------
00040 
00041 Cache::Cache( const CacheOptions& options ) :
00042 osg::Object( true ),
00043 _options(options)
00044 {
00045     //NOP
00046 }
00047 
00048 Cache::Cache(const Cache& rhs, const osg::CopyOp& op) :
00049 osg::Object(rhs),
00050 _refURI( rhs._refURI )
00051 {
00052     //NOP
00053 }
00054 
00055 bool
00056 Cache::getHeightField( const TileKey& key, const CacheSpec& spec, osg::ref_ptr<const osg::HeightField>& out_hf )
00057 {
00058         //Try to get an image from the cache
00059         osg::ref_ptr<const osg::Image> image;
00060     if ( getImage( key, spec, image ) )
00061         {
00062                 OE_DEBUG << LC << "Read cached heightfield " << std::endl;
00063                 ImageToHeightFieldConverter conv;
00064                 out_hf = conv.convert(image.get());
00065         return out_hf.valid();
00066         }
00067     else return false;
00068 }
00069 
00070 void
00071 Cache::setHeightField( const TileKey& key, const CacheSpec& spec, const osg::HeightField* hf)
00072 {
00073         ImageToHeightFieldConverter conv;
00074         //Take a reference to the returned image so it gets deleted properly
00075     osg::ref_ptr<osg::Image> image = conv.convert(hf);
00076         setImage( key, spec, image.get() );
00077 }
00078 
00079 //------------------------------------------------------------------------
00080 
00081 #undef  LC
00082 #define LC "[DiskCache] "
00083 
00084 static Threading::ReadWriteMutex s_mutex;
00085 
00086 DiskCache::DiskCache( const DiskCacheOptions& options ) :
00087 Cache( options ),
00088 _options( options )
00089 {
00090     setName( "tilecache" );
00091     _writeWorldFilesOverride = getenv("OSGEARTH_WRITE_WORLD_FILES") != 0L;
00092 }
00093 
00094 DiskCache::DiskCache( const DiskCache& rhs, const osg::CopyOp& op ) :
00095 Cache( rhs, op ),
00096 _layerPropertiesCache( rhs._layerPropertiesCache ),
00097 _writeWorldFilesOverride( rhs._writeWorldFilesOverride ),
00098 _options( rhs._options )
00099 {
00100     //NOP
00101 }
00102 
00103 bool
00104 DiskCache::isCached(const osgEarth::TileKey& key, const CacheSpec& spec ) const
00105 {
00106         //Check to see if the file for this key exists
00107         std::string filename = getFilename( key, spec );
00108     return osgDB::fileExists(filename);
00109 }
00110 
00111 std::string
00112 DiskCache::getPath() const
00113 {
00114     if ( _options.path().empty() || _refURI.empty() )
00115         return _options.path();
00116     else
00117     {
00118         if (osgDB::containsServerAddress( _refURI )) return _options.path();
00119         return osgEarth::getFullPath( _refURI, _options.path() );
00120     }
00121 }
00122 
00123 std::string
00124 DiskCache::getFilename(const osgEarth::TileKey& key, const CacheSpec& spec ) const
00125 {
00126         unsigned int level, x, y;
00127     level = key.getLevelOfDetail() +1;
00128     key.getTileXY( x, y );
00129 
00130     unsigned int numCols, numRows;
00131     key.getProfile()->getNumTiles(level, numCols, numRows);
00132 
00133     // need to invert the y-tile index
00134     y = numRows - y - 1;
00135 
00136     char buf[2048];
00137     sprintf( buf, "%s/%s/%02d/%03d/%03d/%03d/%03d/%03d/%03d.%s",
00138         getPath().c_str(),
00139         spec.cacheId().c_str(),
00140         level,
00141         (x / 1000000),
00142         (x / 1000) % 1000,
00143         (x % 1000),
00144         (y / 1000000),
00145         (y / 1000) % 1000,
00146         (y % 1000),
00147         spec.format().c_str());
00148 
00149     return buf;
00150 }
00151 
00152 bool
00153 DiskCache::getImage( const TileKey& key, const CacheSpec& spec, osg::ref_ptr<const osg::Image>& out_image )
00154 {
00155         std::string filename = getFilename(key, spec);
00156 
00157     //If the path doesn't contain a zip file, check to see that it actually exists on disk
00158     if (!osgEarth::isZipPath(filename))
00159     {
00160         if (!osgDB::fileExists(filename)) 
00161             return false;
00162     }
00163 
00164     {
00165         Threading::ScopedReadLock lock(s_mutex);
00166         out_image = osgDB::readImageFile( filename );
00167     }
00168 
00169     return out_image.valid();
00170 }
00171 
00175 void 
00176 DiskCache::setImage( const TileKey& key, const CacheSpec& spec, const osg::Image* image)
00177 {
00178         std::string filename = getFilename( key, spec );
00179     std::string path = osgDB::getFilePath(filename);
00180         std::string extension = spec.format();
00181 
00182         //If the format is empty, see if we can glean an extension from the image filename
00183         if (extension.empty())
00184         {
00185                 if (!image->getFileName().empty())
00186                 {
00187                         extension = osgDB::getFileExtension( image->getFileName() );
00188                 }
00189         }
00190 
00191         //If the format is STILL empty, just use PNG
00192         if (extension.empty())
00193         {
00194                 extension = "png";
00195         }
00196 
00197     // serialize cache writes.
00198     Threading::ScopedWriteLock lock(s_mutex);
00199 
00200     //If the path doesn't currently exist or we can't create the path, don't cache the file
00201     if (!osgDB::fileExists(path) && !osgEarth::isZipPath(path) && !osgDB::makeDirectory(path))
00202     {
00203         OE_WARN << LC << "Couldn't create path " << path << std::endl;
00204     }
00205 
00206     std::string ext = osgDB::getFileExtension(filename);
00207 
00208     if ( _options.writeWorldFiles() == true || _writeWorldFilesOverride )
00209     {
00210         //Write out the world file along side the image
00211         double minx, miny, maxx, maxy;
00212         key.getExtent().getBounds(minx, miny, maxx, maxy);
00213 
00214         std::string baseFilename = osgDB::getNameLessExtension(filename);
00215 
00216         /*
00217         Determine the correct extension for the file type.  Typically, world file extensions
00218         consist of the first letter of the extension, followed by the third, then the letter "w".
00219         For instance a jpg file's world file would be a jgw file.
00220         */
00221         std::string worldFileExt = "wld";
00222         if (ext.size() >= 3)
00223         {
00224             worldFileExt[0] = ext[0];
00225             worldFileExt[1] = ext[2];
00226             worldFileExt[2] = 'w';
00227         }
00228         std::string worldFileName = baseFilename + std::string(".") + worldFileExt;
00229         std::ofstream worldFile;
00230         worldFile.open(worldFileName.c_str());
00231 
00232         double x_units_per_pixel = (maxx - minx) / (double)image->s();
00233         double y_units_per_pixel = -(maxy - miny) / (double)image->t();
00234         worldFile << std::fixed << std::setprecision(10)
00235             //X direction units per pixel
00236             << x_units_per_pixel << std::endl
00237             //Rotation about the y axis, in our case 0
00238             << "0" << std::endl
00239             //Rotation about the x axis, in our case 0
00240             << "0" << std::endl
00241             //Y direction units per pixel, typically negative
00242             << y_units_per_pixel << std::endl
00243             //X coordinate of the center of the upper left pixel
00244             << minx + 0.5 * x_units_per_pixel << std::endl
00245             //Y coordinate of the upper left pixel
00246             << maxy + 0.5 * y_units_per_pixel;
00247         worldFile.close();
00248     }
00249 
00250     bool writingJpeg = (ext == "jpg" || ext == "jpeg");
00251 
00252         osg::ref_ptr<osgDB::ReaderWriter::Options> op = new osgDB::ReaderWriter::Options();
00253         op->setOptionString(_options.imageWriterPluginOptions().value());
00254 
00255         //If we are trying to write a non RGB image to JPEG, convert it to RGB before we write it
00256     if ((image->getPixelFormat() != GL_RGB) && writingJpeg)
00257     {
00258                 //Take a reference so the converted image will be deleted
00259                 osg::ref_ptr<osg::Image> rgb = ImageUtils::convertToRGB8( image );
00260                 if (rgb.valid())
00261                 {
00262                         osgDB::writeImageFile(*rgb.get(), filename, op.get());
00263                 }
00264     }
00265     else
00266     {
00267         osgDB::writeImageFile(*image, filename, op.get());
00268     }
00269 }
00270 
00271 std::string
00272 DiskCache::getTMSPath(const std::string& cacheId) const
00273 {
00274     return getPath() + std::string("/") + cacheId + std::string("/tms.xml");
00275 }
00276 
00277 void DiskCache::storeProperties(const CacheSpec& spec,
00278                                 const Profile* profile,
00279                                 unsigned int tile_size)
00280 {
00281         osg::ref_ptr<TileMap> tileMap = TileMap::create("", profile, spec.format(), tile_size, tile_size);
00282     tileMap->setTitle( spec.name() );
00283         std::string path = getTMSPath( spec.cacheId() );
00284     OE_DEBUG << LC << "Writing TMS file to  " << path << std::endl;
00285         TileMapReaderWriter::write( tileMap.get(), path );
00286 }
00287 
00288 bool
00289 DiskCache::loadProperties(const std::string&           cacheId,
00290                           CacheSpec&                   out_spec,
00291                           osg::ref_ptr<const Profile>& out_profile,
00292                           unsigned int&                out_tileSize)
00293 {
00294         //Try to get it from the cache.
00295         LayerPropertiesCache::const_iterator i = _layerPropertiesCache.find(cacheId);
00296         if ( i != _layerPropertiesCache.end())
00297         {
00298         out_spec = CacheSpec(cacheId, i->second._format);
00299                 out_tileSize = i->second._tile_size;
00300         out_profile = i->second._profile.get();
00301         return true;
00302         }
00303 
00304         osg::ref_ptr<TileMap> tileMap;
00305 
00306         std::string path = getTMSPath( cacheId );
00307         OE_INFO << LC << "Metadata file is " << path << std::endl;
00308 
00309         if (osgDB::fileExists( path ) )
00310         {
00311                 osg::ref_ptr<TileMap> tileMap = TileMapReaderWriter::read(path, NULL);
00312                 if (tileMap.valid())
00313                 {
00314                         LayerProperties props;
00315                         props._format = tileMap->getFormat().getExtension();
00316                         props._tile_size = tileMap->getFormat().getWidth();
00317                         props._profile = tileMap->createProfile();
00318 
00319                         _layerPropertiesCache[cacheId] = props;
00320 
00321             out_spec = CacheSpec( cacheId, props._format );
00322             out_profile = props._profile.get();
00323             out_tileSize = props._tile_size;
00324             return true;
00325                 }
00326         else
00327         {
00328             OE_WARN << LC << "Failed to load cache metadata from " << path << std::endl;
00329         }
00330         }
00331         return false;
00332 }
00333 
00334 //------------------------------------------------------------------------
00335 
00336 #undef  LC
00337 #define LC "[MemCache] "
00338 
00339 MemCache::MemCache( int maxSize ):
00340 _maxNumTilesInCache( maxSize )
00341 {
00342     setName( "mem" );
00343 }
00344 
00345 MemCache::MemCache( const MemCache& rhs, const osg::CopyOp& op ) :
00346 _maxNumTilesInCache( rhs._maxNumTilesInCache )
00347 {
00348 }
00349 
00350 unsigned int
00351 MemCache::getMaxNumTilesInCache() const
00352 {
00353         return _maxNumTilesInCache;
00354 }
00355 
00356 void
00357 MemCache::setMaxNumTilesInCache(unsigned int max)
00358 {
00359         _maxNumTilesInCache = max;
00360 }
00361 
00362 bool
00363 MemCache::getImage(const osgEarth::TileKey& key, const CacheSpec& spec, osg::ref_ptr<const osg::Image>& out_image )
00364 {
00365     osg::ref_ptr<const osg::Object> result;
00366     if ( getObject(key, spec, result) )
00367     {
00368         out_image = dynamic_cast<const osg::Image*>( result.get() );
00369         return out_image.valid();
00370     }
00371     else return false;
00372 }
00373 
00374 void
00375 MemCache::setImage(const osgEarth::TileKey& key, const CacheSpec& spec, const osg::Image* image)
00376 {
00377     setObject( key, spec, ImageUtils::cloneImage(image) );
00378 }
00379 
00380 bool
00381 MemCache::getHeightField( const TileKey& key,const CacheSpec& spec, osg::ref_ptr<const osg::HeightField>& out_hf )
00382 {
00383     osg::ref_ptr<const osg::Object> result;
00384     if ( getObject(key, spec, result) )
00385     {
00386         out_hf = dynamic_cast<const osg::HeightField*>(result.get());
00387         return out_hf.valid();
00388     }
00389     else return false;
00390 }
00391 
00392 void
00393 MemCache::setHeightField( const TileKey& key, const CacheSpec& spec, const osg::HeightField* hf)
00394 {
00395     setObject( key, spec, new osg::HeightField(*hf) );
00396 }
00397 
00398 bool
00399 MemCache::purge( const std::string& cacheId, int olderThan, bool async )
00400 {
00401     //TODO: can this be a ReadWriteLock instead?
00402 
00403     // MemCache does not support timestamps or async, so just clear it out altogether.
00404     OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
00405 
00406     // MemCache does not support cacheId...
00407     _keyToIterMap.clear();
00408     _objects.clear();
00409 
00410     return true;
00411 }
00412 
00413 bool
00414 MemCache::getObject( const TileKey& key, const CacheSpec& spec, osg::ref_ptr<const osg::Object>& output )
00415 {
00416   OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
00417 
00418   std::string id = key.str() + spec.cacheId();
00419   KeyToIteratorMap::iterator itr = _keyToIterMap.find(id);
00420   if (itr != _keyToIterMap.end())
00421   {
00422     CachedObject entry = *itr->second;
00423     _objects.erase(itr->second);
00424     _objects.push_front(entry);
00425     itr->second = _objects.begin();
00426     output = itr->second->_object.get();
00427     return output.valid();
00428   }
00429   return false;
00430 }
00431 
00432 void
00433 MemCache::setObject( const TileKey& key, const CacheSpec& spec, const osg::Object* referenced )
00434 {
00435   OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
00436 
00437   std::string id = key.str() + spec.cacheId();
00438 
00439   _objects.push_front(CachedObject());
00440   CachedObject& entry = _objects.front();
00441 
00442   //CachedObject entry;
00443   entry._object = referenced;
00444   entry._key = id;
00445   //_objects.push_front(entry);
00446 
00447   _keyToIterMap[id] = _objects.begin();
00448 
00449   if (_objects.size() > _maxNumTilesInCache)
00450   {
00451       _keyToIterMap.erase( _objects.back()._key );
00452       _objects.pop_back();
00453     //CachedObject toRemove = _objects.back();
00454     //_objects.pop_back();
00455     //_keyToIterMap.erase(toRemove._key);
00456   }
00457 }
00458 
00459 bool
00460 MemCache::isCached(const osgEarth::TileKey& key, const CacheSpec& spec) const
00461 {
00462     OpenThreads::ScopedLock<OpenThreads::Mutex> lock( const_cast<MemCache*>(this)->_mutex);
00463     std::string id = key.str() + spec.cacheId();
00464     return _keyToIterMap.find(id) != _keyToIterMap.end();
00465         //osg::ref_ptr<osg::Image> image = getImage(key,spec);
00466         //return image.valid();
00467 }
00468 
00469 //------------------------------------------------------------------------
00470 
00471 #undef  LC
00472 #define LC "[TMSCache] "
00473 
00474 TMSCache::TMSCache( const TMSCacheOptions& options ) :
00475 DiskCache( options ),
00476 _options( options )
00477 {
00478     setName( "tms" );
00479 }
00480 
00481 TMSCache::TMSCache( const TMSCache& rhs, const osg::CopyOp& op ) :
00482 DiskCache( rhs, op ),
00483 _options( rhs._options )
00484 {
00485     //nop
00486 }
00487 
00488 std::string
00489 TMSCache::getFilename( const TileKey& key,const CacheSpec& spec ) const
00490 {
00491         unsigned int x,y;
00492     key.getTileXY(x, y);
00493 
00494     unsigned int lod = key.getLevelOfDetail();
00495     
00496     unsigned int numCols, numRows;
00497     key.getProfile()->getNumTiles(lod, numCols, numRows);
00498     if ( _options.invertY() == false )
00499     {
00500         y = numRows - y - 1;
00501     }
00502 
00503     std::stringstream buf;
00504     buf << getPath() << "/" << spec.cacheId() << "/" << lod << "/" << x << "/" << y << "." << spec.format();
00505         std::string bufStr;
00506         bufStr = buf.str();
00507     return bufStr;
00508 }
00509 
00510 //------------------------------------------------------------------------
00511 
00512 #undef  LC
00513 #define LC "[CacheFactory] "
00514 #define CACHE_OPTIONS_TAG "__osgEarth::CacheOptions"
00515 
00516 Cache*
00517 CacheFactory::create( const CacheOptions& options)
00518 {
00519     osg::ref_ptr<Cache> result =0L;
00520     OE_INFO << LC << "Initializing cache of type \"" << options.getDriver() << "\"" << std::endl;
00521 
00522     if ( options.getDriver().empty() )
00523     {
00524         OE_WARN << LC << "ILLEGAL: no driver set in cache options" << std::endl;
00525     }
00526     else if ( options.getDriver() == "tms" )
00527     {
00528         result = new TMSCache( options );
00529     }
00530     else if ( options.getDriver() == "tilecache" )
00531     {
00532         result = new DiskCache( options );
00533     }
00534     else // try to load from a plugin
00535     {
00536         osg::ref_ptr<osgDB::ReaderWriter::Options> rwopt = new osgDB::ReaderWriter::Options();
00537         rwopt->setPluginData( CACHE_OPTIONS_TAG, (void*)&options );
00538 
00539         std::string driverExt = ".osgearth_cache_" + options.getDriver();
00540         osgDB::ReaderWriter::ReadResult rr = osgDB::readObjectFile( driverExt, rwopt.get() );
00541         result = dynamic_cast<Cache*>( rr.getObject() );
00542         if ( !result )
00543         {
00544             OE_WARN << LC << "Failed to load cache plugin for type \"" << options.getDriver() << "\"" << std::endl;
00545         }
00546     }
00547     return result.release();
00548 }
00549 
00550 //------------------------------------------------------------------------
00551 
00552 const CacheOptions&
00553 CacheDriver::getCacheOptions( const osgDB::ReaderWriter::Options* rwopt ) const 
00554 {
00555     return *static_cast<const CacheOptions*>( rwopt->getPluginData( CACHE_OPTIONS_TAG ) );
00556 }
00557 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines