void CacheSeed::seed( Map* map ) { // We must do this to avoid an error message in OpenSceneGraph b/c the findWrapper method doesn't appear to be threadsafe. // This really isn't a big deal b/c this only effects data that is already cached. osgDB::ObjectWrapper* wrapper = osgDB::Registry::instance()->getObjectWrapperManager()->findWrapper( "osg::Image" ); osg::Timer_t startTime = osg::Timer::instance()->tick(); if ( !map->getCache() ) { OE_WARN << LC << "Warning: No cache defined; aborting." << std::endl; return; } std::vector<TileKey> keys; map->getProfile()->getRootKeys(keys); //Add the map's entire extent if we don't have one specified. if (_extents.empty()) { addExtent( map->getProfile()->getExtent() ); } bool hasCaches = false; int src_min_level = INT_MAX; unsigned int src_max_level = 0; MapFrame mapf( map, Map::TERRAIN_LAYERS, "CacheSeed::seed" ); //Assumes the the TileSource will perform the caching for us when we call createImage for( ImageLayerVector::const_iterator i = mapf.imageLayers().begin(); i != mapf.imageLayers().end(); i++ ) { ImageLayer* layer = i->get(); TileSource* src = layer->getTileSource(); const ImageLayerOptions& opt = layer->getImageLayerOptions(); if ( layer->isCacheOnly() ) { OE_WARN << LC << "Warning: Layer \"" << layer->getName() << "\" is set to cache-only; skipping." << std::endl; } else if ( !src ) { OE_WARN << "Warning: Layer \"" << layer->getName() << "\" could not create TileSource; skipping." << std::endl; } //else if ( src->getCachePolicyHint(0L) == CachePolicy::NO_CACHE ) //{ // OE_WARN << LC << "Warning: Layer \"" << layer->getName() << "\" does not support seeding; skipping." << std::endl; //} else if ( !layer->getCache() ) { OE_WARN << LC << "Notice: Layer \"" << layer->getName() << "\" has no cache defined; skipping." << std::endl; } else { hasCaches = true; if (opt.minLevel().isSet() && (int)opt.minLevel().get() < src_min_level) src_min_level = opt.minLevel().get(); if (opt.maxLevel().isSet() && opt.maxLevel().get() > src_max_level) src_max_level = opt.maxLevel().get(); } } for( ElevationLayerVector::const_iterator i = mapf.elevationLayers().begin(); i != mapf.elevationLayers().end(); i++ ) { ElevationLayer* layer = i->get(); TileSource* src = layer->getTileSource(); const ElevationLayerOptions& opt = layer->getElevationLayerOptions(); if ( layer->isCacheOnly() ) { OE_WARN << LC << "Warning: Layer \"" << layer->getName() << "\" is set to cache-only; skipping." << std::endl; } else if (!src) { OE_WARN << "Warning: Layer \"" << layer->getName() << "\" could not create TileSource; skipping." << std::endl; } //else if ( src->getCachePolicyHint(0L) == CachePolicy::NO_CACHE ) //{ // OE_WARN << LC << "Warning: Layer \"" << layer->getName() << "\" does not support seeding; skipping." << std::endl; //} else if ( !layer->getCache() ) { OE_WARN << LC << "Notice: Layer \"" << layer->getName() << "\" has no cache defined; skipping." << std::endl; } else { hasCaches = true; if (opt.minLevel().isSet() && (int)opt.minLevel().get() < src_min_level) src_min_level = opt.minLevel().get(); if (opt.maxLevel().isSet() && opt.maxLevel().get() > src_max_level) src_max_level = opt.maxLevel().get(); } } if ( !hasCaches ) { OE_WARN << LC << "There are either no caches defined in the map, or no sources to cache; aborting." << std::endl; return; } if ( src_max_level > 0 && src_max_level < _maxLevel ) { _maxLevel = src_max_level; } OE_NOTICE << LC << "Maximum cache level will be " << _maxLevel << std::endl; //Estimate the number of tiles _total = 0; CacheEstimator est; est.setMinLevel( _minLevel ); est.setMaxLevel( _maxLevel ); est.setProfile( map->getProfile() ); for (unsigned int i = 0; i < _extents.size(); i++) { est.addExtent( _extents[ i ] ); } _total = est.getNumTiles(); OE_INFO << "Processing ~" << _total << " tiles" << std::endl; // Initialize the operations queue _queue = new osg::OperationQueue; osg::Timer_t endTime = osg::Timer::instance()->tick(); // Start the threads std::vector< osg::ref_ptr< osg::OperationsThread > > threads; for (unsigned int i = 0; i < _numThreads; i++) { osg::OperationsThread* thread = new osg::OperationsThread(); thread->setOperationQueue(_queue.get()); thread->start(); threads.push_back( thread ); } OE_NOTICE << "Startup time " << osg::Timer::instance()->delta_s( startTime, endTime ) << std::endl; // Add the root keys to the queue for (unsigned int i = 0; i < keys.size(); ++i) { //processKey( mapf, keys[i] ); _queue.get()->add( new CacheTileOperation( mapf, *this, keys[i]) ); } bool done = false; while (!done) { OpenThreads::Thread::microSleep(500000); // sleep for half a second done = true; if (_queue->getNumOperationsInQueue() > 0) { done = false; continue; } else { // Make sure no threads are currently working on an operation, which actually might add MORE operations since we are doing a quadtree traversal for (unsigned int i = 0; i < threads.size(); i++) { if (threads[i]->getCurrentOperation()) { done = false; continue; } } } } _total = _completed; if ( _progress.valid()) _progress->reportProgress(_completed, _total, 0, 1, "Finished"); }
osg::Image* CompositeTileSource::createImage(const TileKey& key, ProgressCallback* progress ) { ImageMixVector images; images.reserve(_imageLayers.size()); // Try to get an image from each of the layers for the given key. for (ImageLayerVector::const_iterator itr = _imageLayers.begin(); itr != _imageLayers.end(); ++itr) { ImageLayer* layer = itr->get(); ImageInfo imageInfo; imageInfo.dataInExtents = layer->getTileSource()->hasDataInExtent( key.getExtent() ); imageInfo.opacity = layer->getOpacity(); if (imageInfo.dataInExtents) { GeoImage image = layer->createImage(key, progress); if (image.valid()) { imageInfo.image = image.getImage(); } // If the progress got cancelled or it needs a retry then return NULL to prevent this tile from being built and cached with incomplete or partial data. if (progress && (progress->isCanceled() || progress->needsRetry())) { OE_DEBUG << LC << " createImage was cancelled or needs retry for " << key.str() << std::endl; return 0L; } } images.push_back(imageInfo); } // Determine the output texture size to use based on the image that were creatd. unsigned numValidImages = 0; osg::Vec2s textureSize; for (unsigned int i = 0; i < images.size(); i++) { ImageInfo& info = images[i]; if (info.image.valid()) { if (numValidImages == 0) { textureSize.set( info.image->s(), info.image->t()); } numValidImages++; } } // Create fallback images if we have some valid data but not for all the layers if (numValidImages > 0 && numValidImages < images.size()) { for (unsigned int i = 0; i < images.size(); i++) { ImageInfo& info = images[i]; ImageLayer* layer = _imageLayers[i].get(); if (!info.image.valid() && info.dataInExtents) { TileKey parentKey = key.createParentKey(); GeoImage image; while (!image.valid() && parentKey.valid()) { image = layer->createImage(parentKey, progress); if (image.valid()) { break; } // If the progress got cancelled or it needs a retry then return NULL to prevent this tile from being built and cached with incomplete or partial data. if (progress && (progress->isCanceled() || progress->needsRetry())) { OE_DEBUG << LC << " createImage was cancelled or needs retry for " << key.str() << std::endl; return 0L; } parentKey = parentKey.createParentKey(); } if (image.valid()) { // TODO: Bilinear options? bool bilinear = layer->isCoverage() ? false : true; GeoImage cropped = image.crop( key.getExtent(), true, textureSize.x(), textureSize.y(), bilinear); info.image = cropped.getImage(); } } } } // Now finally create the output image. //Recompute the number of valid images numValidImages = 0; for (unsigned int i = 0; i < images.size(); i++) { ImageInfo& info = images[i]; if (info.image.valid()) numValidImages++; } if ( progress && progress->isCanceled() ) { return 0L; } else if ( numValidImages == 0 ) { return 0L; } else if ( numValidImages == 1 ) { //We only have one valid image, so just return it and don't bother with compositing for (unsigned int i = 0; i < images.size(); i++) { ImageInfo& info = images[i]; if (info.image.valid()) return info.image.release(); } return 0L; } else { osg::Image* result = 0; for (unsigned int i = 0; i < images.size(); i++) { ImageInfo& imageInfo = images[i]; if (!result) { if (imageInfo.image.valid()) { result = new osg::Image( *imageInfo.image.get()); } } else { if (imageInfo.image.valid()) { ImageUtils::mix( result, imageInfo.image.get(), imageInfo.opacity ); } } } return result; } }
void CacheSeed::seed( Map* map ) { if ( !map->getCache() ) { OE_WARN << LC << "Warning: No cache defined; aborting." << std::endl; return; } std::vector<TileKey> keys; map->getProfile()->getRootKeys(keys); //Add the map's entire extent if we don't have one specified. if (_extents.empty()) { addExtent( map->getProfile()->getExtent() ); } bool hasCaches = false; int src_min_level = INT_MAX; unsigned int src_max_level = 0; MapFrame mapf( map, Map::TERRAIN_LAYERS, "CacheSeed::seed" ); //Assumes the the TileSource will perform the caching for us when we call createImage for( ImageLayerVector::const_iterator i = mapf.imageLayers().begin(); i != mapf.imageLayers().end(); i++ ) { ImageLayer* layer = i->get(); TileSource* src = layer->getTileSource(); const ImageLayerOptions& opt = layer->getImageLayerOptions(); if ( layer->isCacheOnly() ) { OE_WARN << LC << "Warning: Layer \"" << layer->getName() << "\" is set to cache-only; skipping." << std::endl; } else if ( !src ) { OE_WARN << "Warning: Layer \"" << layer->getName() << "\" could not create TileSource; skipping." << std::endl; } //else if ( src->getCachePolicyHint(0L) == CachePolicy::NO_CACHE ) //{ // OE_WARN << LC << "Warning: Layer \"" << layer->getName() << "\" does not support seeding; skipping." << std::endl; //} else if ( !layer->getCache() ) { OE_WARN << LC << "Notice: Layer \"" << layer->getName() << "\" has no cache defined; skipping." << std::endl; } else { hasCaches = true; if (opt.minLevel().isSet() && (int)opt.minLevel().get() < src_min_level) src_min_level = opt.minLevel().get(); if (opt.maxLevel().isSet() && opt.maxLevel().get() > src_max_level) src_max_level = opt.maxLevel().get(); } } for( ElevationLayerVector::const_iterator i = mapf.elevationLayers().begin(); i != mapf.elevationLayers().end(); i++ ) { ElevationLayer* layer = i->get(); TileSource* src = layer->getTileSource(); const ElevationLayerOptions& opt = layer->getElevationLayerOptions(); if ( layer->isCacheOnly() ) { OE_WARN << LC << "Warning: Layer \"" << layer->getName() << "\" is set to cache-only; skipping." << std::endl; } else if (!src) { OE_WARN << "Warning: Layer \"" << layer->getName() << "\" could not create TileSource; skipping." << std::endl; } //else if ( src->getCachePolicyHint(0L) == CachePolicy::NO_CACHE ) //{ // OE_WARN << LC << "Warning: Layer \"" << layer->getName() << "\" does not support seeding; skipping." << std::endl; //} else if ( !layer->getCache() ) { OE_WARN << LC << "Notice: Layer \"" << layer->getName() << "\" has no cache defined; skipping." << std::endl; } else { hasCaches = true; if (opt.minLevel().isSet() && (int)opt.minLevel().get() < src_min_level) src_min_level = opt.minLevel().get(); if (opt.maxLevel().isSet() && opt.maxLevel().get() > src_max_level) src_max_level = opt.maxLevel().get(); } } if ( !hasCaches ) { OE_WARN << LC << "There are either no caches defined in the map, or no sources to cache; aborting." << std::endl; return; } if ( src_max_level > 0 && src_max_level < _maxLevel ) { _maxLevel = src_max_level; } OE_NOTICE << LC << "Maximum cache level will be " << _maxLevel << std::endl; //Estimate the number of tiles _total = 0; CacheEstimator est; est.setMinLevel( _minLevel ); est.setMaxLevel( _maxLevel ); est.setProfile( map->getProfile() ); for (unsigned int i = 0; i < _extents.size(); i++) { est.addExtent( _extents[ i ] ); } _total = est.getNumTiles(); OE_INFO << "Processing ~" << _total << " tiles" << std::endl; for (unsigned int i = 0; i < keys.size(); ++i) { processKey( mapf, keys[i] ); } _total = _completed; if ( _progress.valid()) _progress->reportProgress(_completed, _total, 0, 1, "Finished"); }
void Map::calculateProfile() { if ( !_profile.valid() ) { osg::ref_ptr<const Profile> userProfile; if ( _mapOptions.profile().isSet() ) { userProfile = Profile::create( _mapOptions.profile().value() ); } if ( _mapOptions.coordSysType() == MapOptions::CSTYPE_GEOCENTRIC ) { if ( userProfile.valid() ) { if ( userProfile->isOK() && userProfile->getSRS()->isGeographic() ) { _profile = userProfile.get(); } else { OE_WARN << LC << "Map is geocentric, but the configured profile SRS (" << userProfile->getSRS()->getName() << ") is not geographic; " << "it will be ignored." << std::endl; } } if ( !_profile.valid() ) { // by default, set a geocentric map to use global-geodetic WGS84. _profile = osgEarth::Registry::instance()->getGlobalGeodeticProfile(); } } else if ( _mapOptions.coordSysType() == MapOptions::CSTYPE_GEOCENTRIC_CUBE ) { //If the map type is a Geocentric Cube, set the profile to the cube profile. _profile = osgEarth::Registry::instance()->getCubeProfile(); } else // CSTYPE_PROJECTED { if ( userProfile.valid() ) { _profile = userProfile.get(); } } // At this point, if we don't have a profile we need to search tile sources until we find one. if ( !_profile.valid() ) { Threading::ScopedReadLock lock( _mapDataMutex ); for( ImageLayerVector::iterator i = _imageLayers.begin(); i != _imageLayers.end() && !_profile.valid(); i++ ) { ImageLayer* layer = i->get(); if ( layer->getTileSource() ) { _profile = layer->getTileSource()->getProfile(); } } for( ElevationLayerVector::iterator i = _elevationLayers.begin(); i != _elevationLayers.end() && !_profile.valid(); i++ ) { ElevationLayer* layer = i->get(); if ( layer->getTileSource() ) { _profile = layer->getTileSource()->getProfile(); } } } // convert the profile to Plate Carre if necessary. if (_profile.valid() && _profile->getSRS()->isGeographic() && getMapOptions().coordSysType() == MapOptions::CSTYPE_PROJECTED ) { OE_INFO << LC << "Projected display with geographic SRS; activating Plate Carre mode" << std::endl; _profile = _profile->overrideSRS( _profile->getSRS()->createPlateCarreGeographicSRS() ); } // finally, fire an event if the profile has been set. if ( _profile.valid() ) { OE_INFO << LC << "Map profile is: " << _profile->toString() << std::endl; for( MapCallbackList::iterator i = _mapCallbacks.begin(); i != _mapCallbacks.end(); i++ ) { i->get()->onMapInfoEstablished( MapInfo(this) ); } } else { OE_WARN << LC << "Warning, not yet able to establish a map profile!" << std::endl; } } if ( _profile.valid() ) { // tell all the loaded layers what the profile is, as a hint { Threading::ScopedWriteLock lock( _mapDataMutex ); for( ImageLayerVector::iterator i = _imageLayers.begin(); i != _imageLayers.end(); i++ ) { ImageLayer* layer = i->get(); if ( layer->getEnabled() == true ) { layer->setTargetProfileHint( _profile.get() ); } } for( ElevationLayerVector::iterator i = _elevationLayers.begin(); i != _elevationLayers.end(); i++ ) { ElevationLayer* layer = i->get(); if ( layer->getEnabled() ) { layer->setTargetProfileHint( _profile.get() ); } } } // create a "proxy" profile to use when querying elevation layers with a vertical datum if ( _profile->getSRS()->getVerticalDatum() != 0L ) { ProfileOptions po = _profile->toProfileOptions(); po.vsrsString().unset(); _profileNoVDatum = Profile::create(po); } else { _profileNoVDatum = _profile; } } }
void CacheSeed::seed( Map* map ) { if ( !map->getCache() ) { OE_WARN << LC << "Warning: No cache defined; aborting." << std::endl; return; } std::vector<TileKey> keys; map->getProfile()->getRootKeys(keys); //Add the map's entire extent if we don't have one specified. if (_extents.empty()) { addExtent( map->getProfile()->getExtent() ); } bool hasCaches = false; int src_min_level = INT_MAX; unsigned int src_max_level = 0; MapFrame mapf( map, Map::TERRAIN_LAYERS, "CacheSeed::seed" ); //Assumes the the TileSource will perform the caching for us when we call createImage for( ImageLayerVector::const_iterator i = mapf.imageLayers().begin(); i != mapf.imageLayers().end(); i++ ) { ImageLayer* layer = i->get(); TileSource* src = layer->getTileSource(); const ImageLayerOptions& opt = layer->getImageLayerOptions(); if ( layer->isCacheOnly() ) { OE_WARN << LC << "Warning: Layer \"" << layer->getName() << "\" is set to cache-only; skipping." << std::endl; } else if ( !src ) { OE_WARN << "Warning: Layer \"" << layer->getName() << "\" could not create TileSource; skipping." << std::endl; } //else if ( src->getCachePolicyHint(0L) == CachePolicy::NO_CACHE ) //{ // OE_WARN << LC << "Warning: Layer \"" << layer->getName() << "\" does not support seeding; skipping." << std::endl; //} else if ( !layer->getCache() ) { OE_WARN << LC << "Notice: Layer \"" << layer->getName() << "\" has no cache defined; skipping." << std::endl; } else { hasCaches = true; if (opt.minLevel().isSet() && (int)opt.minLevel().get() < src_min_level) src_min_level = opt.minLevel().get(); if (opt.maxLevel().isSet() && opt.maxLevel().get() > src_max_level) src_max_level = opt.maxLevel().get(); } } for( ElevationLayerVector::const_iterator i = mapf.elevationLayers().begin(); i != mapf.elevationLayers().end(); i++ ) { ElevationLayer* layer = i->get(); TileSource* src = layer->getTileSource(); const ElevationLayerOptions& opt = layer->getElevationLayerOptions(); if ( layer->isCacheOnly() ) { OE_WARN << LC << "Warning: Layer \"" << layer->getName() << "\" is set to cache-only; skipping." << std::endl; } else if (!src) { OE_WARN << "Warning: Layer \"" << layer->getName() << "\" could not create TileSource; skipping." << std::endl; } //else if ( src->getCachePolicyHint(0L) == CachePolicy::NO_CACHE ) //{ // OE_WARN << LC << "Warning: Layer \"" << layer->getName() << "\" does not support seeding; skipping." << std::endl; //} else if ( !layer->getCache() ) { OE_WARN << LC << "Notice: Layer \"" << layer->getName() << "\" has no cache defined; skipping." << std::endl; } else { hasCaches = true; if (opt.minLevel().isSet() && (int)opt.minLevel().get() < src_min_level) src_min_level = opt.minLevel().get(); if (opt.maxLevel().isSet() && opt.maxLevel().get() > src_max_level) src_max_level = opt.maxLevel().get(); } } if ( !hasCaches ) { OE_WARN << LC << "There are either no caches defined in the map, or no sources to cache; aborting." << std::endl; return; } if ( src_max_level > 0 && src_max_level < _maxLevel ) { _maxLevel = src_max_level; } OE_NOTICE << LC << "Maximum cache level will be " << _maxLevel << std::endl; osg::Timer_t startTime = osg::Timer::instance()->tick(); //Estimate the number of tiles _total = 0; for (unsigned int level = _minLevel; level <= _maxLevel; level++) { double coverageRatio = 0.0; if (_extents.empty()) { unsigned int wide, high; map->getProfile()->getNumTiles( level, wide, high ); _total += (wide * high); } else { for (std::vector< GeoExtent >::const_iterator itr = _extents.begin(); itr != _extents.end(); itr++) { const GeoExtent& extent = *itr; double boundsArea = extent.area(); TileKey ll = map->getProfile()->createTileKey(extent.xMin(), extent.yMin(), level); TileKey ur = map->getProfile()->createTileKey(extent.xMax(), extent.yMax(), level); if (!ll.valid() || !ur.valid()) continue; int tilesWide = ur.getTileX() - ll.getTileX() + 1; int tilesHigh = ll.getTileY() - ur.getTileY() + 1; int tilesAtLevel = tilesWide * tilesHigh; //OE_NOTICE << "Tiles at level " << level << "=" << tilesAtLevel << std::endl; /* bool hasData = false; for (ImageLayerVector::const_iterator itr = mapf.imageLayers().begin(); itr != mapf.imageLayers().end(); itr++) { TileSource* src = itr->get()->getTileSource(); if (src) { if (src->hasDataAtLOD( level )) { //Compute the percent coverage of this dataset on the current extent if (src->getDataExtents().size() > 0) { double cov = 0.0; for (unsigned int j = 0; j < src->getDataExtents().size(); j++) { GeoExtent b = src->getDataExtents()[j].transform( extent.getSRS()); GeoExtent intersection = b.intersectionSameSRS( extent ); if (intersection.isValid()) { double coverage = intersection.area() / boundsArea; cov += coverage; //Assumes the extents aren't overlapping } } if (coverageRatio < cov) coverageRatio = cov; } else { //We have no way of knowing how much coverage we have coverageRatio = 1.0; } hasData = true; break; } } } for (ElevationLayerVector::const_iterator itr = mapf.elevationLayers().begin(); itr != mapf.elevationLayers().end(); itr++) { TileSource* src = itr->get()->getTileSource(); if (src) { if (src->hasDataAtLOD( level )) { //Compute the percent coverage of this dataset on the current extent if (src->getDataExtents().size() > 0) { double cov = 0.0; for (unsigned int j = 0; j < src->getDataExtents().size(); j++) { GeoExtent b = src->getDataExtents()[j].transform( extent.getSRS()); GeoExtent intersection = b.intersectionSameSRS( extent ); if (intersection.isValid()) { double coverage = intersection.area() / boundsArea; cov += coverage; //Assumes the extents aren't overlapping } } if (coverageRatio < cov) coverageRatio = cov; } else { //We have no way of knowing how much coverage we have coverageRatio = 1.0; } hasData = true; break; } } } //Adjust the coverage ratio by a fudge factor to try to keep it from being too small, //tiles are either processed or not and the ratio is exact so will cover tiles partially //and potentially be too small double adjust = 4.0; coverageRatio = osg::clampBetween(coverageRatio * adjust, 0.0, 1.0); //OE_NOTICE << level << " CoverageRatio = " << coverageRatio << std::endl; if (hasData) { _total += (int)ceil(coverageRatio * (double)tilesAtLevel ); } */ _total += tilesAtLevel; } } } osg::Timer_t endTime = osg::Timer::instance()->tick(); //OE_NOTICE << "Counted tiles in " << osg::Timer::instance()->delta_s(startTime, endTime) << " s" << std::endl; OE_INFO << "Processing ~" << _total << " tiles" << std::endl; for (unsigned int i = 0; i < keys.size(); ++i) { processKey( mapf, keys[i] ); } _total = _completed; if ( _progress.valid()) _progress->reportProgress(_completed, _total, 0, 1, "Finished"); }
void Map::calculateProfile() { if ( !_profile.valid() ) { osg::ref_ptr<const Profile> userProfile; if ( _mapOptions.profile().isSet() ) { userProfile = Profile::create( _mapOptions.profile().value() ); } if ( _mapOptions.coordSysType() == MapOptions::CSTYPE_GEOCENTRIC ) { if ( userProfile.valid() ) { if ( userProfile->isOK() && userProfile->getSRS()->isGeographic() ) { _profile = userProfile.get(); } else { OE_WARN << LC << "Map is geocentric, but the configured profile does not " << "have a geographic SRS. Falling back on default.." << std::endl; } } if ( !_profile.valid() ) { // by default, set a geocentric map to use global-geodetic WGS84. _profile = osgEarth::Registry::instance()->getGlobalGeodeticProfile(); } } else if ( _mapOptions.coordSysType() == MapOptions::CSTYPE_GEOCENTRIC_CUBE ) { //If the map type is a Geocentric Cube, set the profile to the cube profile. _profile = osgEarth::Registry::instance()->getCubeProfile(); } else // CSTYPE_PROJECTED { if ( userProfile.valid() ) { _profile = userProfile.get(); } } // At this point, if we don't have a profile we need to search tile sources until we find one. if ( !_profile.valid() ) { Threading::ScopedReadLock lock( _mapDataMutex ); for( ImageLayerVector::iterator i = _imageLayers.begin(); i != _imageLayers.end() && !_profile.valid(); i++ ) { ImageLayer* layer = i->get(); if ( layer->getTileSource() ) { _profile = layer->getTileSource()->getProfile(); } } for( ElevationLayerVector::iterator i = _elevationLayers.begin(); i != _elevationLayers.end() && !_profile.valid(); i++ ) { ElevationLayer* layer = i->get(); if ( layer->getTileSource() ) { _profile = layer->getTileSource()->getProfile(); } } } // finally, fire an event if the profile has been set. if ( _profile.valid() ) { OE_INFO << LC << "Map profile is: " << _profile->toString() << std::endl; for( MapCallbackList::iterator i = _mapCallbacks.begin(); i != _mapCallbacks.end(); i++ ) { i->get()->onMapInfoEstablished( MapInfo(this) ); } } else { OE_WARN << LC << "Warning, not yet able to establish a map profile!" << std::endl; } } if ( _profile.valid() ) { // tell all the loaded layers what the profile is, as a hint { Threading::ScopedWriteLock lock( _mapDataMutex ); for( ImageLayerVector::iterator i = _imageLayers.begin(); i != _imageLayers.end(); i++ ) { ImageLayer* layer = i->get(); layer->setTargetProfileHint( _profile.get() ); } for( ElevationLayerVector::iterator i = _elevationLayers.begin(); i != _elevationLayers.end(); i++ ) { ElevationLayer* layer = i->get(); layer->setTargetProfileHint( _profile.get() ); } } } }