bool TerrainLayer::isCached(const TileKey& key) const { CacheBin* bin = const_cast<TerrainLayer*>(this)->getCacheBin( key.getProfile() ); if ( !bin ) return false; TimeStamp minTime = this->getCachePolicy().getMinAcceptTime(); return bin->getRecordStatus( key.str(), minTime ) == CacheBin::STATUS_OK; }
bool TerrainLayer::isCached(const TileKey& key) const { // first consult the policy: if ( getCachePolicy() == CachePolicy::NO_CACHE ) return false; else if ( getCachePolicy() == CachePolicy::CACHE_ONLY ) return true; // next check for a bin: CacheBin* bin = const_cast<TerrainLayer*>(this)->getCacheBin( key.getProfile() ); if ( !bin ) return false; return bin->getRecordStatus( key.str() ) == CacheBin::STATUS_OK; }
GeoHeightField ElevationLayer::createHeightField(const TileKey& key, ProgressCallback* progress ) { METRIC_SCOPED_EX("ElevationLayer::createHeightField", 2, "key", key.str().c_str(), "name", getName().c_str()); if (getStatus().isError()) { return GeoHeightField::INVALID; } // If the layer is disabled, bail out. if ( getEnabled() == false ) { return GeoHeightField::INVALID; } GeoHeightField result; osg::ref_ptr<osg::HeightField> hf; osg::ref_ptr<NormalMap> normalMap; // Check the memory cache first bool fromMemCache = false; // cache key combines the key with the full signature (incl vdatum) // the cache key combines the Key and the horizontal profile. std::string cacheKey = Cache::makeCacheKey( Stringify() << key.str() << "-" << key.getProfile()->getHorizSignature(), "elevation"); const CachePolicy& policy = getCacheSettings()->cachePolicy().get(); if ( _memCache.valid() ) { CacheBin* bin = _memCache->getOrCreateDefaultBin(); ReadResult cacheResult = bin->readObject(cacheKey, 0L); if ( cacheResult.succeeded() ) { result = GeoHeightField( static_cast<osg::HeightField*>(cacheResult.releaseObject()), key.getExtent()); fromMemCache = true; } } if ( !result.valid() ) { // See if there's a persistent cache. CacheBin* cacheBin = getCacheBin( key.getProfile() ); // Can we continue? Only if either: // a) there is a valid tile source plugin; // b) a tile source is not expected, meaning the subclass overrides getHeightField; or // c) we are in cache-only mode and there is a valid cache bin. bool canContinue = getTileSource() || !isTileSourceExpected() || (policy.isCacheOnly() && cacheBin != 0L); if (!canContinue) { disable("Error: layer does not have a valid TileSource, cannot create heightfield"); return GeoHeightField::INVALID; } // validate the existance of a valid layer profile. if ( !policy.isCacheOnly() && !getProfile() ) { disable("Could not establish a valid profile.. did you set one?"); return GeoHeightField::INVALID; } // Now attempt to read from the cache. Since the cached data is stored in the // map profile, we can try this first. bool fromCache = false; osg::ref_ptr< osg::HeightField > cachedHF; if ( cacheBin && policy.isCacheReadable() ) { ReadResult r = cacheBin->readObject(cacheKey, 0L); if ( r.succeeded() ) { bool expired = policy.isExpired(r.lastModifiedTime()); cachedHF = r.get<osg::HeightField>(); if ( cachedHF && validateHeightField(cachedHF.get()) ) { if (!expired) { hf = cachedHF; fromCache = true; } } } } // if we're cache-only, but didn't get data from the cache, fail silently. if ( !hf.valid() && policy.isCacheOnly() ) { return GeoHeightField::INVALID; } if ( !hf.valid() ) { if ( !isKeyInLegalRange(key) ) return GeoHeightField::INVALID; // If no tile source is expected, create a height field by calling // the raw inheritable method. if (!isTileSourceExpected()) { createImplementation(key, hf, normalMap, progress); //hf = createHeightFieldImplementation(key, progress); } else { // bad tilesource? fail if ( !getTileSource() || !getTileSource()->isOK() ) return GeoHeightField::INVALID; // build a HF from the TileSource. //hf = createHeightFieldImplementation( key, progress ); createImplementation(key, hf, normalMap, progress); } // Check for cancelation before writing to a cache if (progress && progress->isCanceled()) { return GeoHeightField::INVALID; } // validate it to make sure it's legal. if ( hf.valid() && !validateHeightField(hf.get()) ) { OE_WARN << LC << "Driver " << getTileSource()->getName() << " returned an illegal heightfield" << std::endl; hf = 0L; // to fall back on cached data if possible. } // cache if necessary if ( hf && cacheBin && !fromCache && policy.isCacheWriteable() ) { cacheBin->write(cacheKey, hf.get(), 0L); } // We have an expired heightfield from the cache and no new data from the TileSource. So just return the cached data. if (!hf.valid() && cachedHF.valid()) { OE_DEBUG << LC << "Using cached but expired heightfield for " << key.str() << std::endl; hf = cachedHF; } if ( !hf.valid() ) { return GeoHeightField::INVALID; } // Set up the heightfield params. double minx, miny, maxx, maxy; key.getExtent().getBounds(minx, miny, maxx, maxy); hf->setOrigin( osg::Vec3d( minx, miny, 0.0 ) ); double dx = (maxx - minx)/(double)(hf->getNumColumns()-1); double dy = (maxy - miny)/(double)(hf->getNumRows()-1); hf->setXInterval( dx ); hf->setYInterval( dy ); hf->setBorderWidth( 0 ); } if ( hf.valid() ) { result = GeoHeightField( hf.get(), normalMap.get(), key.getExtent() ); } } // Check for cancelation before writing to a cache: if ( progress && progress->isCanceled() ) { return GeoHeightField::INVALID; } // post-processing -- must be done before caching because it may alter the heightfield data if ( result.valid() && !fromMemCache && hf.valid() ) { if ( options().noDataPolicy() == NODATA_MSL ) { // requested VDatum: const VerticalDatum* outputVDatum = key.getExtent().getSRS()->getVerticalDatum(); const Geoid* geoid = 0L; // if there's an output vdatum, just set all invalid's to zero MSL. if ( outputVDatum == 0L ) { // if the output is geodetic (HAE), but the input has a geoid, // use that geoid to populate the invalid data at sea level. const VerticalDatum* profileDatum = getProfile()->getSRS()->getVerticalDatum(); if ( profileDatum ) geoid = profileDatum->getGeoid(); } HeightFieldUtils::resolveInvalidHeights( hf.get(), result.getExtent(), NO_DATA_VALUE, geoid ); } } // write to mem cache if needed: if ( result.valid() && !fromMemCache && _memCache.valid() ) { CacheBin* bin = _memCache->getOrCreateDefaultBin(); bin->write(cacheKey, result.getHeightField(), 0L); } return result; }
GeoImage ImageLayer::createImageInKeyProfile(const TileKey& key, ProgressCallback* progress) { if (getStatus().isError()) { return GeoImage::INVALID; } // If the layer is disabled, bail out. if ( !getEnabled() ) { return GeoImage::INVALID; } // Make sure the request is in range. if ( !isKeyInRange(key) ) { return GeoImage::INVALID; } GeoImage result; OE_DEBUG << LC << "create image for \"" << key.str() << "\", ext= " << key.getExtent().toString() << std::endl; // the cache key combines the Key and the horizontal profile. std::string cacheKey = Stringify() << key.str() << "_" << key.getProfile()->getHorizSignature(); const CachePolicy& policy = getCacheSettings()->cachePolicy().get(); // Check the layer L2 cache first if ( _memCache.valid() ) { CacheBin* bin = _memCache->getOrCreateDefaultBin(); ReadResult result = bin->readObject(cacheKey, 0L); if ( result.succeeded() ) return GeoImage(static_cast<osg::Image*>(result.releaseObject()), key.getExtent()); } // locate the cache bin for the target profile for this layer: CacheBin* cacheBin = getCacheBin( key.getProfile() ); // validate that we have either a valid tile source, or we're cache-only. if (getTileSource() || (cacheBin && policy.isCacheOnly())) { //nop = OK. } else { disable("Error: layer does not have a valid TileSource, cannot create image"); return GeoImage::INVALID; } // validate the existance of a valid layer profile (unless we're in cache-only mode, in which // case there is no layer profile) if ( !policy.isCacheOnly() && !getProfile() ) { disable("Could not establish a valid profile"); return GeoImage::INVALID; } osg::ref_ptr< osg::Image > cachedImage; // First, attempt to read from the cache. Since the cached data is stored in the // map profile, we can try this first. if ( cacheBin && policy.isCacheReadable() ) { ReadResult r = cacheBin->readImage(cacheKey, 0L); if ( r.succeeded() ) { cachedImage = r.releaseImage(); ImageUtils::fixInternalFormat( cachedImage.get() ); bool expired = policy.isExpired(r.lastModifiedTime()); if (!expired) { OE_DEBUG << "Got cached image for " << key.str() << std::endl; return GeoImage( cachedImage.get(), key.getExtent() ); } else { OE_DEBUG << "Expired image for " << key.str() << std::endl; } } } // The data was not in the cache. If we are cache-only, fail sliently if ( policy.isCacheOnly() ) { // If it's cache only and we have an expired but cached image, just return it. if (cachedImage.valid()) { return GeoImage( cachedImage.get(), key.getExtent() ); } else { return GeoImage::INVALID; } } // Get an image from the underlying TileSource. result = createImageFromTileSource( key, progress ); // Normalize the image if necessary if ( result.valid() ) { ImageUtils::fixInternalFormat( result.getImage() ); } // memory cache first: if ( result.valid() && _memCache.valid() ) { CacheBin* bin = _memCache->getOrCreateDefaultBin(); bin->write(cacheKey, result.getImage(), 0L); } // 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() && cacheBin && policy.isCacheWriteable()) { if ( key.getExtent() != result.getExtent() ) { OE_INFO << LC << "WARNING! mismatched extents." << std::endl; } cacheBin->write(cacheKey, result.getImage(), 0L); } if ( result.valid() ) { OE_DEBUG << LC << key.str() << " result OK" << std::endl; } else { OE_DEBUG << LC << key.str() << "result INVALID" << std::endl; // We couldn't get an image from the source. So see if we have an expired cached image if (cachedImage.valid()) { OE_DEBUG << LC << "Using cached but expired image for " << key.str() << std::endl; result = GeoImage( cachedImage.get(), key.getExtent()); } } return result; }
GeoImage ImageLayer::createImageInKeyProfile( const TileKey& key, ProgressCallback* progress, bool forceFallback, bool& out_isFallback ) { GeoImage result; out_isFallback = false; // If the layer is disabled, bail out. if ( !getEnabled() ) { return GeoImage::INVALID; } // Check the max data level, which limits the LOD of available data. if ( _runtimeOptions.maxDataLevel().isSet() && key.getLOD() > _runtimeOptions.maxDataLevel().value() ) { return GeoImage::INVALID; } // Check for a "Minumum level" setting on this layer. If we are before the // min level, just return the empty image. Do not cache empties if ( _runtimeOptions.minLevel().isSet() && key.getLOD() < _runtimeOptions.minLevel().value() ) { return GeoImage( _emptyImage.get(), key.getExtent() ); } // Check for a "Minimum resolution" setting on the layer. If we are before the // min resolution, return the empty image. Do not cache empties. if ( _runtimeOptions.minResolution().isSet() ) { double keyres = key.getExtent().width() / getTileSize(); double keyresInLayerProfile = key.getProfile()->getSRS()->transformUnits(keyres, getProfile()->getSRS()); if ( keyresInLayerProfile > _runtimeOptions.minResolution().value() ) { return GeoImage( _emptyImage.get(), key.getExtent() ); } } OE_DEBUG << LC << "create image for \"" << key.str() << "\", ext= " << key.getExtent().toString() << std::endl; // locate the cache bin for the target profile for this layer: CacheBin* cacheBin = getCacheBin( key.getProfile() ); // validate that we have either a valid tile source, or we're cache-only. if ( ! (getTileSource() || (isCacheOnly() && cacheBin) ) ) { OE_WARN << LC << "Error: layer does not have a valid TileSource, cannot create image " << std::endl; _runtimeOptions.enabled() = false; return GeoImage::INVALID; } // validate the existance of a valid layer profile (unless we're in cache-only mode, in which // case there is no layer profile) if ( !isCacheOnly() && !getProfile() ) { OE_WARN << LC << "Could not establish a valid profile" << std::endl; _runtimeOptions.enabled() = false; return GeoImage::INVALID; } // First, attempt to read from the cache. Since the cached data is stored in the // map profile, we can try this first. if ( cacheBin && getCachePolicy().isCacheReadable() ) { ReadResult r = cacheBin->readImage( key.str(), getCachePolicy().getMinAcceptTime() ); if ( r.succeeded() ) { ImageUtils::normalizeImage( r.getImage() ); return GeoImage( r.releaseImage(), key.getExtent() ); } //else if ( r.code() == ReadResult::RESULT_EXPIRED ) //{ // OE_INFO << LC << getName() << " : " << key.str() << " record expired!" << std::endl; //} } // The data was not in the cache. If we are cache-only, fail sliently if ( isCacheOnly() ) { return GeoImage::INVALID; } // Get an image from the underlying TileSource. result = createImageFromTileSource( key, progress, forceFallback, out_isFallback ); // 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() && //JB: Removed the check to not write out fallback data. If you have a low resolution base dataset (max lod 3) and a high resolution insert (max lod 22) // then the low res data needs to "fallback" from LOD 4 - 22 so you can display the high res inset. If you don't cache these intermediate tiles then // performance can suffer generating all those fallback tiles, especially if you have to do reprojection or mosaicing. //!out_isFallback && cacheBin && getCachePolicy().isCacheWriteable() ) { if ( key.getExtent() != result.getExtent() ) { OE_INFO << LC << "WARNING! mismatched extents." << std::endl; } cacheBin->write( key.str(), result.getImage() ); //OE_INFO << LC << "WRITING " << key.str() << " to the cache." << std::endl; } if ( result.valid() ) { OE_DEBUG << LC << key.str() << " result OK" << std::endl; } else { OE_DEBUG << LC << key.str() << "result INVALID" << std::endl; } return result; }
GeoHeightField ElevationLayer::createHeightField(const TileKey& key, ProgressCallback* progress ) { osg::HeightField* result = 0L; // If the layer is disabled, bail out. if ( _runtimeOptions.enabled().isSetTo( false ) ) { return GeoHeightField::INVALID; } CacheBin* cacheBin = getCacheBin( key.getProfile() ); // validate that we have either a valid tile source, or we're cache-only. if ( ! (getTileSource() || (isCacheOnly() && cacheBin) ) ) { OE_WARN << LC << "Error: layer does not have a valid TileSource, cannot create heightfield" << std::endl; _runtimeOptions.enabled() = false; return GeoHeightField::INVALID; } // validate the existance of a valid layer profile. if ( !isCacheOnly() && !getProfile() ) { OE_WARN << LC << "Could not establish a valid profile" << std::endl; _runtimeOptions.enabled() = false; return GeoHeightField::INVALID; } // First, attempt to read from the cache. Since the cached data is stored in the // map profile, we can try this first. bool fromCache = false; if ( cacheBin && getCachePolicy().isCacheReadable() ) { ReadResult r = cacheBin->readObject( key.str() ); if ( r.succeeded() ) { result = r.release<osg::HeightField>(); if ( result ) fromCache = true; } } // if we're cache-only, but didn't get data from the cache, fail silently. if ( !result && isCacheOnly() ) { return GeoHeightField::INVALID; } if ( !result ) { // bad tilesource? fail if ( !getTileSource() || !getTileSource()->isOK() ) return GeoHeightField::INVALID; if ( !isKeyValid(key) ) return GeoHeightField::INVALID; // build a HF from the TileSource. result = createHeightFieldFromTileSource( key, progress ); } // cache if necessary if ( result && cacheBin && !fromCache && getCachePolicy().isCacheWriteable() ) { cacheBin->write( key.str(), result ); } if ( result ) { // Set up the heightfield so we don't have to worry about it later double minx, miny, maxx, maxy; key.getExtent().getBounds(minx, miny, maxx, maxy); result->setOrigin( osg::Vec3d( minx, miny, 0.0 ) ); double dx = (maxx - minx)/(double)(result->getNumColumns()-1); double dy = (maxy - miny)/(double)(result->getNumRows()-1); result->setXInterval( dx ); result->setYInterval( dy ); result->setBorderWidth( 0 ); } return result ? GeoHeightField( result, key.getExtent() ) : GeoHeightField::INVALID; }
GeoHeightField ElevationLayer::createHeightField(const TileKey& key, ProgressCallback* progress ) { GeoHeightField result; osg::ref_ptr<osg::HeightField> hf; // If the layer is disabled, bail out. if ( getEnabled() == false ) { return GeoHeightField::INVALID; } // Check the memory cache first if ( _memCache.valid() ) { CacheBin* bin = _memCache->getOrCreateBin( key.getProfile()->getFullSignature() ); ReadResult cacheResult = bin->readObject(key.str() ); if ( cacheResult.succeeded() ) { result = GeoHeightField( static_cast<osg::HeightField*>(cacheResult.releaseObject()), key.getExtent()); } //_memCache->dumpStats(key.getProfile()->getFullSignature()); } if ( !result.valid() ) { // See if there's a persistent cache. CacheBin* cacheBin = getCacheBin( key.getProfile() ); // validate that we have either a valid tile source, or we're cache-only. if ( ! (getTileSource() || (isCacheOnly() && cacheBin) ) ) { OE_WARN << LC << "Error: layer does not have a valid TileSource, cannot create heightfield" << std::endl; _runtimeOptions.enabled() = false; return GeoHeightField::INVALID; } // validate the existance of a valid layer profile. if ( !isCacheOnly() && !getProfile() ) { OE_WARN << LC << "Could not establish a valid profile" << std::endl; _runtimeOptions.enabled() = false; return GeoHeightField::INVALID; } // Now attempt to read from the cache. Since the cached data is stored in the // map profile, we can try this first. bool fromCache = false; osg::ref_ptr< osg::HeightField > cachedHF; if ( cacheBin && getCachePolicy().isCacheReadable() ) { ReadResult r = cacheBin->readObject( key.str() ); if ( r.succeeded() ) { bool expired = getCachePolicy().isExpired(r.lastModifiedTime()); cachedHF = r.get<osg::HeightField>(); if ( cachedHF && validateHeightField(cachedHF) ) { if (!expired) { hf = cachedHF; fromCache = true; } } } } // if we're cache-only, but didn't get data from the cache, fail silently. if ( !hf.valid() && isCacheOnly() ) { return GeoHeightField::INVALID; } if ( !hf.valid() ) { // bad tilesource? fail if ( !getTileSource() || !getTileSource()->isOK() ) return GeoHeightField::INVALID; if ( !isKeyInRange(key) ) return GeoHeightField::INVALID; // build a HF from the TileSource. hf = createHeightFieldFromTileSource( key, progress ); // validate it to make sure it's legal. if ( hf.valid() && !validateHeightField(hf.get()) ) { OE_WARN << LC << "Driver " << getTileSource()->getName() << " returned an illegal heightfield" << std::endl; hf = 0L; // to fall back on cached data if possible. } // memory cache first: if ( hf && _memCache.valid() ) { CacheBin* bin = _memCache->getOrCreateBin( key.getProfile()->getFullSignature() ); bin->write(key.str(), hf.get()); } // cache if necessary if ( hf && cacheBin && !fromCache && getCachePolicy().isCacheWriteable() ) { cacheBin->write( key.str(), hf ); } // We have an expired heightfield from the cache and no new data from the TileSource. So just return the cached data. if (!hf.valid() && cachedHF.valid()) { OE_DEBUG << LC << "Using cached but expired heightfield for " << key.str() << std::endl; hf = cachedHF; } if ( !hf.valid() ) { return GeoHeightField::INVALID; } // Set up the heightfield so we don't have to worry about it later double minx, miny, maxx, maxy; key.getExtent().getBounds(minx, miny, maxx, maxy); hf->setOrigin( osg::Vec3d( minx, miny, 0.0 ) ); double dx = (maxx - minx)/(double)(hf->getNumColumns()-1); double dy = (maxy - miny)/(double)(hf->getNumRows()-1); hf->setXInterval( dx ); hf->setYInterval( dy ); hf->setBorderWidth( 0 ); } if ( hf.valid() ) { result = GeoHeightField( hf.get(), key.getExtent() ); } } // post-processing: if ( result.valid() ) { if ( _runtimeOptions.noDataPolicy() == NODATA_MSL ) { // requested VDatum: const VerticalDatum* outputVDatum = key.getExtent().getSRS()->getVerticalDatum(); const Geoid* geoid = 0L; // if there's an output vdatum, just set all invalid's to zero MSL. if ( outputVDatum == 0L ) { // if the output is geodetic (HAE), but the input has a geoid, // use that geoid to populate the invalid data at sea level. const VerticalDatum* profileDatum = getProfile()->getSRS()->getVerticalDatum(); if ( profileDatum ) geoid = profileDatum->getGeoid(); } HeightFieldUtils::resolveInvalidHeights( result.getHeightField(), result.getExtent(), NO_DATA_VALUE, geoid ); } } return result; }
bool TerrainLayer::isCached(const TileKey& key) const { CacheBin* bin = const_cast<TerrainLayer*>(this)->getCacheBin( key.getProfile() ); return bin ? bin->isCached(key.str()) : false; }
GeoImage ImageLayer::createImageInKeyProfile(const TileKey& key, ProgressCallback* progress) { GeoImage result; // If the layer is disabled, bail out. if ( !getEnabled() ) { return GeoImage::INVALID; } // Make sure the request is in range. if ( !isKeyInRange(key) ) { return GeoImage::INVALID; } OE_DEBUG << LC << "create image for \"" << key.str() << "\", ext= " << key.getExtent().toString() << std::endl; // Check the layer L2 cache first if ( _memCache.valid() ) { CacheBin* bin = _memCache->getOrCreateBin( key.getProfile()->getFullSignature() ); ReadResult result = bin->readObject(key.str(), 0); if ( result.succeeded() ) return GeoImage(static_cast<osg::Image*>(result.releaseObject()), key.getExtent()); //_memCache->dumpStats(key.getProfile()->getFullSignature()); } // locate the cache bin for the target profile for this layer: CacheBin* cacheBin = getCacheBin( key.getProfile() ); // validate that we have either a valid tile source, or we're cache-only. if ( ! (getTileSource() || (isCacheOnly() && cacheBin) ) ) { OE_WARN << LC << "Error: layer does not have a valid TileSource, cannot create image " << std::endl; _runtimeOptions.enabled() = false; return GeoImage::INVALID; } // validate the existance of a valid layer profile (unless we're in cache-only mode, in which // case there is no layer profile) if ( !isCacheOnly() && !getProfile() ) { OE_WARN << LC << "Could not establish a valid profile" << std::endl; _runtimeOptions.enabled() = false; return GeoImage::INVALID; } // First, attempt to read from the cache. Since the cached data is stored in the // map profile, we can try this first. if ( cacheBin && getCachePolicy().isCacheReadable() ) { ReadResult r = cacheBin->readImage( key.str(), getCachePolicy().getMinAcceptTime() ); if ( r.succeeded() ) { ImageUtils::normalizeImage( r.getImage() ); return GeoImage( r.releaseImage(), key.getExtent() ); } } // The data was not in the cache. If we are cache-only, fail sliently if ( isCacheOnly() ) { return GeoImage::INVALID; } // Get an image from the underlying TileSource. result = createImageFromTileSource( key, progress ); // Normalize the image if necessary if ( result.valid() ) { ImageUtils::normalizeImage( result.getImage() ); } // memory cache first: if ( result.valid() && _memCache.valid() ) { CacheBin* bin = _memCache->getOrCreateBin( key.getProfile()->getFullSignature() ); bin->write(key.str(), 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() && cacheBin && getCachePolicy().isCacheWriteable() ) { if ( key.getExtent() != result.getExtent() ) { OE_INFO << LC << "WARNING! mismatched extents." << std::endl; } cacheBin->write( key.str(), result.getImage() ); } if ( result.valid() ) { OE_DEBUG << LC << key.str() << " result OK" << std::endl; } else { OE_DEBUG << LC << key.str() << "result INVALID" << std::endl; } return result; }
CacheBin* TerrainLayer::getCacheBin(const Profile* profile) { if ( !_openCalled ) { OE_WARN << LC << "Illegal- called getCacheBin() before layer is open.. did you call open()?\n"; return 0L; } CacheSettings* cacheSettings = getCacheSettings(); if (!cacheSettings) return 0L; if (cacheSettings->cachePolicy()->isCacheDisabled()) return 0L; CacheBin* bin = cacheSettings->getCacheBin(); if (!bin) return 0L; // does the metadata need initializing? std::string metaKey = getMetadataKey(profile); Threading::ScopedMutexLock lock(_mutex); CacheBinMetadataMap::iterator i = _cacheBinMetadata.find(metaKey); if (i == _cacheBinMetadata.end()) { //std::string cacheId = _runtimeOptions->cacheId().get(); // read the metadata record from the cache bin: ReadResult rr = bin->readString(metaKey, _readOptions.get()); osg::ref_ptr<CacheBinMetadata> meta; bool metadataOK = false; if (rr.succeeded()) { // Try to parse the metadata record: Config conf; conf.fromJSON(rr.getString()); meta = new CacheBinMetadata(conf); if (meta->isOK()) { metadataOK = true; // verify that the cache if compatible with the open tile source: if ( getTileSource() && getProfile() ) { //todo: check the profile too if ( meta->_sourceDriver.get() != getTileSource()->getOptions().getDriver() ) { OE_WARN << LC << "Layer \"" << getName() << "\" is requesting a \"" << getTileSource()->getOptions().getDriver() << "\" cache, but a \"" << meta->_sourceDriver.get() << "\" cache exists at the specified location. " << "The cache will ignored for this layer.\n"; cacheSettings->cachePolicy() = CachePolicy::NO_CACHE; return 0L; } } // if not, see if we're in cache-only mode and still need a profile: else if (cacheSettings->cachePolicy()->isCacheOnly() && !_profile.valid()) { // in cacheonly mode, create a profile from the first cache bin accessed // (they SHOULD all be the same...) setProfile( Profile::create(meta->_sourceProfile.get()) ); _tileSize = meta->_sourceTileSize.get(); } bin->setMetadata(meta.get()); } else { OE_WARN << LC << "Metadata appears to be corrupt.\n"; } } if (!metadataOK) { // cache metadata does not exist, so try to create it. if ( getProfile() ) { meta = new CacheBinMetadata(); // no existing metadata; create some. meta->_cacheBinId = _runtimeCacheId; meta->_sourceName = this->getName(); meta->_sourceTileSize = getTileSize(); meta->_sourceProfile = getProfile()->toProfileOptions(); meta->_cacheProfile = profile->toProfileOptions(); meta->_cacheCreateTime = DateTime().asTimeStamp(); meta->_dataExtents = getDataExtents(); if (getTileSource()) { meta->_sourceDriver = getTileSource()->getOptions().getDriver(); } // store it in the cache bin. std::string data = meta->getConfig().toJSON(false); osg::ref_ptr<StringObject> temp = new StringObject(data); bin->write(metaKey, temp.get(), _readOptions.get()); bin->setMetadata(meta.get()); } else if ( cacheSettings->cachePolicy()->isCacheOnly() ) { disable(Stringify() << "Failed to open a cache for layer " "because cache_only policy is in effect and bin [" << _runtimeCacheId << "] " "could not be located."); return 0L; } else { OE_WARN << LC << "Failed to create cache bin [" << _runtimeCacheId << "] " "because there is no valid profile." << std::endl; cacheSettings->cachePolicy() = CachePolicy::NO_CACHE; return 0L; } } // If we loaded a profile from the cache metadata, apply the overrides: applyProfileOverrides(); if (meta.valid()) { _cacheBinMetadata[metaKey] = meta.get(); OE_DEBUG << LC << "Established metadata for cache bin [" << _runtimeCacheId << "]" << std::endl; } } return bin; }
const Status& TerrainLayer::open() { if ( !_openCalled ) { // Call base class if (VisibleLayer::open().isError()) return getStatus(); // Create an L2 mem cache that sits atop the main cache, if necessary. // For now: use the same L2 cache size at the driver. int l2CacheSize = options().driver()->L2CacheSize().get(); // See if it was overridden with an env var. char const* l2env = ::getenv( "OSGEARTH_L2_CACHE_SIZE" ); if ( l2env ) { l2CacheSize = as<int>( std::string(l2env), 0 ); OE_INFO << LC << "L2 cache size set from environment = " << l2CacheSize << "\n"; } // Env cache-only mode also disables the L2 cache. char const* noCacheEnv = ::getenv( "OSGEARTH_MEMORY_PROFILE" ); if ( noCacheEnv ) { l2CacheSize = 0; } // Initialize the l2 cache if it's size is > 0 if ( l2CacheSize > 0 ) { _memCache = new MemCache( l2CacheSize ); } // create the unique cache ID for the cache bin. //std::string cacheId; if (options().cacheId().isSet() && !options().cacheId()->empty()) { // user expliticy set a cacheId in the terrain layer options. // this appears to be a NOP; review for removal -gw _runtimeCacheId = options().cacheId().get(); } else { // system will generate a cacheId from the layer configuration. Config hashConf = options().getConfig(); // remove non-data properties. hashConf.remove("name"); hashConf.remove("enabled"); hashConf.remove("cacheid"); hashConf.remove("cache_only"); hashConf.remove("cache_enabled"); hashConf.remove("cache_policy"); hashConf.remove("visible"); hashConf.remove("l2_cache_size"); OE_DEBUG << "hashConfFinal = " << hashConf.toJSON(true) << std::endl; unsigned hash = osgEarth::hashString(hashConf.toJSON()); _runtimeCacheId = Stringify() << std::hex << std::setw(8) << std::setfill('0') << hash; } // Now that we know the cache ID, establish the cache settings for this Layer. // Start by cloning whatever CacheSettings were inherited in the read options // (typically from the Map). CacheSettings* oldSettings = CacheSettings::get(_readOptions.get()); _cacheSettings = oldSettings ? new CacheSettings(*oldSettings) : new CacheSettings(); // Integrate a cache policy from this Layer's options: _cacheSettings->integrateCachePolicy(options().cachePolicy()); // If you created the layer with a pre-created tile source, it will already by set. if (!_tileSource.valid()) { osg::ref_ptr<TileSource> ts; // as long as we're not in cache-only mode, try to create the TileSource. if (_cacheSettings->cachePolicy()->isCacheOnly()) { OE_INFO << LC << "Opening in cache-only mode\n"; } else if (isTileSourceExpected()) { // Initialize the tile source once and only once. ts = createAndOpenTileSource(); } // If we loaded a tile source, give it some information about caching // if appropriate. if (ts.valid()) { if (_cacheSettings->isCacheEnabled()) { // read the cache policy hint from the tile source unless user expressly set // a policy in the initialization options. In other words, the hint takes // ultimate priority (even over the Registry override) unless expressly // overridden in the layer options! refreshTileSourceCachePolicyHint( ts.get() ); // Unless the user has already configured an expiration policy, use the "last modified" // timestamp of the TileSource to set a minimum valid cache entry timestamp. const CachePolicy& cp = options().cachePolicy().get(); if ( !cp.minTime().isSet() && !cp.maxAge().isSet() && ts->getLastModifiedTime() > 0) { // The "effective" policy overrides the runtime policy, but it does not get serialized. _cacheSettings->cachePolicy()->mergeAndOverride( cp ); _cacheSettings->cachePolicy()->minTime() = ts->getLastModifiedTime(); OE_INFO << LC << "driver says min valid timestamp = " << DateTime(*cp.minTime()).asRFC1123() << "\n"; } } // All is well - set the tile source. if ( !_tileSource.valid() ) { _tileSource = ts.release(); } } } else { // User supplied the tile source, so attempt to get its profile: setProfile(_tileSource->getProfile() ); if (!_profile.valid()) { setStatus( Status::Error(getName(), "Cannot establish profile") ); } } // Finally, open and activate a caching bin for this layer. if (_cacheSettings->isCacheEnabled()) { CacheBin* bin = _cacheSettings->getCache()->addBin(_runtimeCacheId); if (bin) { _cacheSettings->setCacheBin(bin); OE_INFO << LC << "Cache bin is [" << bin->getID() << "]\n"; } } // Store the updated settings in the read options so we can propagate // them as necessary. _cacheSettings->store(_readOptions.get()); OE_INFO << LC << _cacheSettings->toString() << "\n"; // Done! _openCalled = true; } return getStatus(); }