TileMap* TileMapReaderWriter::read( const std::string& location, const osgDB::ReaderWriter::Options* options ) { TileMap* tileMap = NULL; ReadResult r = URI(location).readString(); if ( r.failed() ) { OE_DEBUG << LC << "Failed to read TMS tile map file from " << location << std::endl; return 0L; } // Read tile map into a Config: Config conf; std::stringstream buf( r.getString() ); conf.fromXML( buf ); // parse that into a tile map: tileMap = TileMapReaderWriter::read( conf ); if (tileMap) { tileMap->setFilename( location ); // record the timestamp (if there is one) in the tilemap. It's not a persistent field // but will help with things like per-session caching. tileMap->setTimeStamp( r.lastModifiedTime() ); } return tileMap; }
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; }
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; }
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; }