ElevationLayer* Map::getElevationLayerByName( const std::string& name ) const { Threading::ScopedReadLock( const_cast<Map*>(this)->_mapDataMutex ); for( ElevationLayerVector::const_iterator i = _elevationLayers.begin(); i != _elevationLayers.end(); ++i ) if ( i->get()->getName() == name ) return i->get(); return 0L; }
void MPTerrainEngineNode::postInitialize( const Map* map, const TerrainOptions& options ) { TerrainEngineNode::postInitialize( map, options ); // Initialize the map frames. We need one for the update thread and one for the // cull thread. Someday we can detect whether these are actually the same thread // (depends on the viewer's threading mode). _update_mapf = new MapFrame( map, Map::MASKED_TERRAIN_LAYERS, "mp-update" ); // merge in the custom options: _terrainOptions.merge( options ); // a shared registry for tile nodes in the scene graph. _liveTiles = new TileNodeRegistry("live"); // set up a registry for quick release: if ( _terrainOptions.quickReleaseGLObjects() == true ) { _deadTiles = new TileNodeRegistry("dead"); } // initialize the model factory: _tileModelFactory = new TileModelFactory(getMap(), _liveTiles.get(), _terrainOptions ); // handle an already-established map profile: if ( _update_mapf->getProfile() ) { // NOTE: this will initialize the map with the startup layers onMapInfoEstablished( MapInfo(map) ); } // populate the terrain with whatever data is in the map to begin with: if ( _terrain ) { this->getTextureCompositor()->reserveTextureImageUnit( _textureImageUnit ); updateShaders(); } // install a layer callback for processing further map actions: map->addMapCallback( new MPTerrainEngineNodeMapCallbackProxy(this) ); // Attach to all of the existing elevation layers ElevationLayerVector elevationLayers; map->getElevationLayers( elevationLayers ); for( ElevationLayerVector::const_iterator i = elevationLayers.begin(); i != elevationLayers.end(); ++i ) { i->get()->addCallback( _elevationCallback.get() ); } // register this instance to the osgDB plugin can find it. registerEngine( this ); // now that we have a map, set up to recompute the bounds dirtyBound(); }
ElevationLayer* Map::getElevationLayerByUID( UID layerUID ) const { Threading::ScopedReadLock( const_cast<Map*>(this)->_mapDataMutex ); for( ElevationLayerVector::const_iterator i = _elevationLayers.begin(); i != _elevationLayers.end(); ++i ) if ( i->get()->getUID() == layerUID ) return i->get(); return 0L; }
Config EarthFileSerializer2::serialize( MapNode* input ) const { Config mapConf("map"); mapConf.set("version", "2"); if ( !input || !input->getMap() ) return mapConf; Map* map = input->getMap(); MapFrame mapf( map, Map::ENTIRE_MODEL ); // the map and node options: Config optionsConf = map->getInitialMapOptions().getConfig(); optionsConf.merge( input->getMapNodeOptions().getConfig() ); mapConf.add( "options", optionsConf ); // the layers for( ImageLayerVector::const_iterator i = mapf.imageLayers().begin(); i != mapf.imageLayers().end(); ++i ) { ImageLayer* layer = i->get(); //Config layerConf = layer->getInitialOptions().getConfig(); Config layerConf = layer->getImageLayerOptions().getConfig(); layerConf.set("name", layer->getName()); layerConf.set("driver", layer->getInitialOptions().driver()->getDriver()); mapConf.add( "image", layerConf ); } for( ElevationLayerVector::const_iterator i = mapf.elevationLayers().begin(); i != mapf.elevationLayers().end(); ++i ) { ElevationLayer* layer = i->get(); //Config layerConf = layer->getInitialOptions().getConfig(); Config layerConf = layer->getElevationLayerOptions().getConfig(); layerConf.set("name", layer->getName()); layerConf.set("driver", layer->getInitialOptions().driver()->getDriver()); mapConf.add( "elevation", layerConf ); } for( ModelLayerVector::const_iterator i = mapf.modelLayers().begin(); i != mapf.modelLayers().end(); ++i ) { ModelLayer* layer = i->get(); Config layerConf = layer->getModelLayerOptions().getConfig(); layerConf.set("name", layer->getName()); layerConf.set("driver", layer->getModelLayerOptions().driver()->getDriver()); mapConf.add( "model", layerConf ); } Config ext = input->externalConfig(); if ( !ext.empty() ) { ext.key() = "external"; mapConf.add( ext ); } return mapConf; }
Revision Map::getElevationLayers( ElevationLayerVector& out_list ) const { out_list.reserve( _elevationLayers.size() ); Threading::ScopedReadLock lock( const_cast<Map*>(this)->_mapDataMutex ); for( ElevationLayerVector::const_iterator i = _elevationLayers.begin(); i != _elevationLayers.end(); ++i ) out_list.push_back( i->get() ); return _dataModelRevision; }
void OSGTerrainEngineNode::postInitialize( const Map* map, const TerrainOptions& options ) { TerrainEngineNode::postInitialize( map, options ); // Initialize the map frames. We need one for the update thread and one for the // cull thread. Someday we can detect whether these are actually the same thread // (depends on the viewer's threading mode). _update_mapf = new MapFrame( map, Map::MASKED_TERRAIN_LAYERS, "osgterrain-update" ); _cull_mapf = new MapFrame( map, Map::TERRAIN_LAYERS, "osgterrain-cull" ); // merge in the custom options: _terrainOptions.merge( options ); // handle an already-established map profile: if ( _update_mapf->getProfile() ) { // NOTE: this will initialize the map with the startup layers onMapInfoEstablished( MapInfo(map) ); } // populate the terrain with whatever data is in the map to begin with: if ( _terrain ) { // update the terrain revision in threaded mode if ( _isStreaming ) { static_cast<StreamingTerrainNode*>(_terrain)->updateTaskServiceThreads( *_update_mapf ); } updateTextureCombining(); } // install a layer callback for processing further map actions: map->addMapCallback( new OSGTerrainEngineNodeMapCallbackProxy(this) ); //Attach to all of the existing elevation layers ElevationLayerVector elevationLayers; map->getElevationLayers( elevationLayers ); for( ElevationLayerVector::const_iterator i = elevationLayers.begin(); i != elevationLayers.end(); ++i ) { i->get()->addCallback( _elevationCallback.get() ); } //Attach a callback to all of the // register me. registerEngine( this ); // now that we have a map, set up to recompute the bounds dirtyBound(); }
bool OSGTileFactory::hasMoreLevels( Map* map, const TileKey& key ) { //Threading::ScopedReadLock lock( map->getMapDataMutex() ); bool more_levels = false; ImageLayerVector imageLayers; map->getImageLayers( imageLayers ); for ( ImageLayerVector::const_iterator i = imageLayers.begin(); i != imageLayers.end(); i++ ) { const ImageLayerOptions& opt = i->get()->getImageLayerOptions(); if ( !opt.maxLevel().isSet() || key.getLevelOfDetail() < (unsigned int)*opt.maxLevel() ) { more_levels = true; break; } } if ( !more_levels ) { ElevationLayerVector elevLayers; map->getElevationLayers( elevLayers ); for( ElevationLayerVector::const_iterator j = elevLayers.begin(); j != elevLayers.end(); j++ ) { const ElevationLayerOptions& opt = j->get()->getElevationLayerOptions(); if ( !opt.maxLevel().isSet() || key.getLevelOfDetail() < (unsigned int)*opt.maxLevel() ) //if ( !j->get()->maxLevel().isSet() || key.getLevelOfDetail() < j->get()->maxLevel().get() ) { more_levels = true; break; } } } return more_levels; }
bool MapFrame::isCached( const TileKey& key ) const { //Check to see if the tile will load fast // Check the imagery layers for( ImageLayerVector::const_iterator i = imageLayers().begin(); i != imageLayers().end(); i++ ) { //If we're cache only we should be fast if (i->get()->isCacheOnly()) continue; osg::ref_ptr< TileSource > source = i->get()->getTileSource(); if (!source.valid()) continue; //If the tile is blacklisted, it should also be fast. if ( source->getBlacklist()->contains( key.getTileId() ) ) continue; //If no data is available on this tile, we'll be fast if ( !source->hasData( key ) ) continue; if ( !i->get()->isCached( key ) ) return false; } for( ElevationLayerVector::const_iterator i = elevationLayers().begin(); i != elevationLayers().end(); ++i ) { //If we're cache only we should be fast if (i->get()->isCacheOnly()) continue; osg::ref_ptr< TileSource > source = i->get()->getTileSource(); if (!source.valid()) continue; //If the tile is blacklisted, it should also be fast. if ( source->getBlacklist()->contains( key.getTileId() ) ) continue; if ( !source->hasData( key ) ) continue; if ( !i->get()->isCached( key ) ) { return false; } } return true; }
void ElevationManager::sync() { if ( _mapf.sync() || _tileSize == 0 || _maxDataLevel == 0 ) { _tileSize = 0; _maxDataLevel = 0; for( ElevationLayerVector::const_iterator i = _mapf.elevationLayers().begin(); i != _mapf.elevationLayers().end(); ++i ) { // we need the maximum tile size int layerTileSize = i->get()->getTileSize(); if ( layerTileSize > _tileSize ) _tileSize = layerTileSize; // we also need the maximum available data level. unsigned int layerMaxDataLevel = i->get()->getMaxDataLevel(); if ( layerMaxDataLevel > _maxDataLevel ) _maxDataLevel = layerMaxDataLevel; } } }
void StreamingTerrainNode::updateTaskServiceThreads( const MapFrame& mapf ) { //Get the maximum elevation weight float elevationWeight = 0.0f; for (ElevationLayerVector::const_iterator itr = mapf.elevationLayers().begin(); itr != mapf.elevationLayers().end(); ++itr) { ElevationLayer* layer = itr->get(); float w = layer->getElevationLayerOptions().loadingWeight().value(); if (w > elevationWeight) elevationWeight = w; } float totalImageWeight = 0.0f; for (ImageLayerVector::const_iterator itr = mapf.imageLayers().begin(); itr != mapf.imageLayers().end(); ++itr) { totalImageWeight += itr->get()->getImageLayerOptions().loadingWeight().value(); } float totalWeight = elevationWeight + totalImageWeight; if (elevationWeight > 0.0f) { //Determine how many threads each layer gets int numElevationThreads = (int)osg::round((float)_numLoadingThreads * (elevationWeight / totalWeight )); OE_INFO << LC << "Elevation Threads = " << numElevationThreads << std::endl; getElevationTaskService()->setNumThreads( numElevationThreads ); } for (ImageLayerVector::const_iterator itr = mapf.imageLayers().begin(); itr != mapf.imageLayers().end(); ++itr) { const TerrainLayerOptions& opt = itr->get()->getImageLayerOptions(); int imageThreads = (int)osg::round((float)_numLoadingThreads * (opt.loadingWeight().value() / totalWeight )); OE_INFO << LC << "Image Threads for " << itr->get()->getName() << " = " << imageThreads << std::endl; getImageryTaskService( itr->get()->getUID() )->setNumThreads( imageThreads ); } }
void RexTerrainEngineNode::postInitialize( const Map* map, const TerrainOptions& options ) { // Force the mercator fast path off, since REX does not support it yet. TerrainOptions myOptions = options; myOptions.enableMercatorFastPath() = false; TerrainEngineNode::postInitialize( map, myOptions ); // Initialize the map frames. We need one for the update thread and one for the // cull thread. Someday we can detect whether these are actually the same thread // (depends on the viewer's threading mode). _update_mapf = new MapFrame( map, Map::ENTIRE_MODEL ); // A callback for overriding bounding boxes for tiles _modifyBBoxCallback = new ModifyBoundingBoxCallback(*_update_mapf); // merge in the custom options: _terrainOptions.merge( myOptions ); // morphing imagery LODs requires we bind parent textures to their own unit. if ( _terrainOptions.morphImagery() == true ) { _requireParentTextures = true; } // Terrain morphing doesn't work in projected maps: if (map->getSRS()->isProjected()) { _terrainOptions.morphTerrain() = false; } // if the envvar for tile expiration is set, overide the options setting const char* val = ::getenv("OSGEARTH_EXPIRATION_THRESHOLD"); if ( val ) { _terrainOptions.expirationThreshold() = as<unsigned>(val, _terrainOptions.expirationThreshold().get()); OE_INFO << LC << "Expiration threshold set by env var = " << _terrainOptions.expirationThreshold().get() << "\n"; } // if the envvar for hires prioritization is set, override the options setting const char* hiresFirst = ::getenv("OSGEARTH_HIGH_RES_FIRST"); if ( hiresFirst ) { _terrainOptions.highResolutionFirst() = true; } // check for normal map generation (required for lighting). if ( _terrainOptions.normalMaps() == true ) { this->_requireNormalTextures = true; } // A shared registry for tile nodes in the scene graph. Enable revision tracking // if requested in the options. Revision tracking lets the registry notify all // live tiles of the current map revision so they can inrementally update // themselves if necessary. _liveTiles = new TileNodeRegistry("live"); _liveTiles->setMapRevision( _update_mapf->getRevision() ); // A resource releaser that will call releaseGLObjects() on expired objects. _releaser = new ResourceReleaser(); this->addChild(_releaser.get()); // A shared geometry pool. _geometryPool = new GeometryPool( _terrainOptions ); _geometryPool->setReleaser( _releaser.get()); this->addChild( _geometryPool.get() ); // Make a tile loader PagerLoader* loader = new PagerLoader( this ); loader->setNumLODs(_terrainOptions.maxLOD().getOrUse(DEFAULT_MAX_LOD)); loader->setMergesPerFrame( _terrainOptions.mergesPerFrame().get() ); for (std::vector<RexTerrainEngineOptions::LODOptions>::const_iterator i = _terrainOptions.lods().begin(); i != _terrainOptions.lods().end(); ++i) { if (i->_lod.isSet()) { loader->setLODPriorityScale(i->_lod.get(), i->_priorityScale.getOrUse(1.0f)); loader->setLODPriorityOffset(i->_lod.get(), i->_priorityOffset.getOrUse(0.0f)); } } _loader = loader; this->addChild( _loader.get() ); // Make a tile unloader _unloader = new UnloaderGroup( _liveTiles.get() ); _unloader->setThreshold( _terrainOptions.expirationThreshold().get() ); _unloader->setReleaser(_releaser.get()); this->addChild( _unloader.get() ); // handle an already-established map profile: MapInfo mapInfo( map ); if ( _update_mapf->getProfile() ) { // NOTE: this will initialize the map with the startup layers onMapInfoEstablished( mapInfo ); } // install a layer callback for processing further map actions: map->addMapCallback( new RexTerrainEngineNodeMapCallbackProxy(this) ); // Prime with existing layers: _batchUpdateInProgress = true; ElevationLayerVector elevationLayers; map->getLayers( elevationLayers ); for( ElevationLayerVector::const_iterator i = elevationLayers.begin(); i != elevationLayers.end(); ++i ) addElevationLayer( i->get() ); ImageLayerVector imageLayers; map->getLayers( imageLayers ); for( ImageLayerVector::iterator i = imageLayers.begin(); i != imageLayers.end(); ++i ) addTileLayer( i->get() ); _batchUpdateInProgress = false; // set up the initial shaders updateState(); // register this instance to the osgDB plugin can find it. registerEngine( this ); // now that we have a map, set up to recompute the bounds dirtyBound(); }
bool MapFrame::isCached( const TileKey& key ) const { // is there a map cache at all? if ( _map->getCache() == 0L ) return false; //Check to see if the tile will load fast // Check the imagery layers for( ImageLayerVector::const_iterator i = imageLayers().begin(); i != imageLayers().end(); i++ ) { const ImageLayer* layer = i->get(); if (!layer->getEnabled()) continue; // If we're cache only we should be fast if (layer->isCacheOnly()) continue; // no-cache mode? always slow if (layer->isNoCache()) return false; // No tile source? skip it osg::ref_ptr< TileSource > source = layer->getTileSource(); if (!source.valid()) continue; //If the tile is blacklisted, it should also be fast. if ( source->getBlacklist()->contains( key ) ) continue; //If no data is available on this tile, we'll be fast if ( !source->hasData( key ) ) continue; if ( !layer->isCached(key) ) return false; } for( ElevationLayerVector::const_iterator i = elevationLayers().begin(); i != elevationLayers().end(); ++i ) { const ElevationLayer* layer = i->get(); if (!layer->getEnabled()) continue; //If we're cache only we should be fast if (layer->isCacheOnly()) continue; // no-cache mode? always high-latency. if (layer->isNoCache()) return false; osg::ref_ptr< TileSource > source = layer->getTileSource(); if (!source.valid()) continue; //If the tile is blacklisted, it should also be fast. if ( source->getBlacklist()->contains( key ) ) continue; if ( !source->hasData( key ) ) continue; if ( !i->get()->isCached( key ) ) return false; } return true; }
void MPTerrainEngineNode::postInitialize( const Map* map, const TerrainOptions& options ) { TerrainEngineNode::postInitialize( map, options ); // Initialize the map frames. We need one for the update thread and one for the // cull thread. Someday we can detect whether these are actually the same thread // (depends on the viewer's threading mode). _update_mapf = new MapFrame( map, Map::MASKED_TERRAIN_LAYERS, "mp-update" ); // merge in the custom options: _terrainOptions.merge( options ); // A shared registry for tile nodes in the scene graph. Enable revision tracking // if requested in the options. Revision tracking lets the registry notify all // live tiles of the current map revision so they can inrementally update // themselves if necessary. _liveTiles = new TileNodeRegistry("live"); _liveTiles->setRevisioningEnabled( _terrainOptions.incrementalUpdate() == true ); _liveTiles->setMapRevision( _update_mapf->getRevision() ); // set up a registry for quick release: if ( _terrainOptions.quickReleaseGLObjects() == true ) { _deadTiles = new TileNodeRegistry("dead"); } // initialize the model factory: _tileModelFactory = new TileModelFactory(_liveTiles.get(), _terrainOptions ); // handle an already-established map profile: if ( _update_mapf->getProfile() ) { // NOTE: this will initialize the map with the startup layers onMapInfoEstablished( MapInfo(map) ); } // install a layer callback for processing further map actions: map->addMapCallback( new MPTerrainEngineNodeMapCallbackProxy(this) ); // Prime with existing layers: _batchUpdateInProgress = true; ElevationLayerVector elevationLayers; map->getElevationLayers( elevationLayers ); for( ElevationLayerVector::const_iterator i = elevationLayers.begin(); i != elevationLayers.end(); ++i ) addElevationLayer( i->get() ); ImageLayerVector imageLayers; map->getImageLayers( imageLayers ); for( ImageLayerVector::iterator i = imageLayers.begin(); i != imageLayers.end(); ++i ) addImageLayer( i->get() ); _batchUpdateInProgress = false; // install some terrain-wide uniforms this->getOrCreateStateSet()->getOrCreateUniform( "oe_min_tile_range_factor", osg::Uniform::FLOAT)->set( *_terrainOptions.minTileRangeFactor() ); // set up the initial shaders updateState(); // register this instance to the osgDB plugin can find it. registerEngine( this ); // now that we have a map, set up to recompute the bounds dirtyBound(); OE_INFO << LC << "Edge normalization is " << (_terrainOptions.normalizeEdges() == true? "ON" : "OFF") << std::endl; }
bool Map::sync( MapFrame& frame ) const { bool result = false; if ( frame._mapDataModelRevision != _dataModelRevision || !frame._initialized ) { // hold the read lock while copying the layer lists. Threading::ScopedReadLock lock( const_cast<Map*>(this)->_mapDataMutex ); if ( frame._parts & IMAGE_LAYERS ) { if ( !frame._initialized ) frame._imageLayers.reserve( _imageLayers.size() ); frame._imageLayers.clear(); if ( frame._copyValidDataOnly ) { for( ImageLayerVector::const_iterator i = _imageLayers.begin(); i != _imageLayers.end(); ++i ) if ( i->get()->getProfile() ) frame._imageLayers.push_back( i->get() ); } else std::copy( _imageLayers.begin(), _imageLayers.end(), std::back_inserter(frame._imageLayers) ); } if ( frame._parts & ELEVATION_LAYERS ) { if ( !frame._initialized ) frame._elevationLayers.reserve( _elevationLayers.size() ); frame._elevationLayers.clear(); if ( frame._copyValidDataOnly ) { for( ElevationLayerVector::const_iterator i = _elevationLayers.begin(); i != _elevationLayers.end(); ++i ) if ( i->get()->getProfile() ) frame._elevationLayers.push_back( i->get() ); } else std::copy( _elevationLayers.begin(), _elevationLayers.end(), std::back_inserter(frame._elevationLayers) ); } if ( frame._parts & MODEL_LAYERS ) { if ( !frame._initialized ) frame._modelLayers.reserve( _modelLayers.size() ); frame._modelLayers.clear(); std::copy( _modelLayers.begin(), _modelLayers.end(), std::back_inserter(frame._modelLayers) ); } if ( frame._parts & MASK_LAYERS ) { if ( !frame._initialized ) frame._maskLayers.reserve( _terrainMaskLayers.size() ); frame._maskLayers.clear(); std::copy( _terrainMaskLayers.begin(), _terrainMaskLayers.end(), std::back_inserter(frame._maskLayers) ); } // sync the revision numbers. frame._initialized = true; frame._mapDataModelRevision = _dataModelRevision; result = true; } return result; }
bool MapFrame::isCached( const osgEarth::TileKey& key ) const { const Profile* mapProfile = getProfile(); //Check the imagery layers for( ImageLayerVector::const_iterator i = imageLayers().begin(); i != imageLayers().end(); i++ ) { ImageLayer* layer = i->get(); osg::ref_ptr< Cache > cache = layer->getCache(); if ( !cache.valid() || !layer->getProfile() ) return false; std::vector< TileKey > keys; if ( mapProfile->isEquivalentTo( layer->getProfile() ) ) { keys.push_back( key ); } else { layer->getProfile()->getIntersectingTiles( key, keys ); } for (unsigned int j = 0; j < keys.size(); ++j) { if ( layer->isKeyValid( keys[j] ) ) { if ( !cache->isCached( keys[j], layer->getCacheSpec() ) ) { return false; } } } } for( ElevationLayerVector::const_iterator i = elevationLayers().begin(); i != elevationLayers().end(); ++i ) { ElevationLayer* layer = i->get(); osg::ref_ptr< Cache > cache = layer->getCache(); if ( !cache.valid() || !layer->getProfile() ) return false; std::vector<TileKey> keys; if ( mapProfile->isEquivalentTo( layer->getProfile() ) ) { keys.push_back( key ); } else { layer->getProfile()->getIntersectingTiles( key, keys ); } for (unsigned int j = 0; j < keys.size(); ++j) { if ( layer->isKeyValid( keys[j] ) ) { if ( !cache->isCached( keys[j], layer->getCacheSpec() ) ) { return false; } } } } return true; }
void MPTerrainEngineNode::postInitialize( const Map* map, const TerrainOptions& options ) { TerrainEngineNode::postInitialize( map, options ); // Initialize the map frames. We need one for the update thread and one for the // cull thread. Someday we can detect whether these are actually the same thread // (depends on the viewer's threading mode). _update_mapf = new MapFrame( map, Map::MASKED_TERRAIN_LAYERS, "mp-update" ); // merge in the custom options: _terrainOptions.merge( options ); // a shared registry for tile nodes in the scene graph. _liveTiles = new TileNodeRegistry("live"); // set up a registry for quick release: if ( _terrainOptions.quickReleaseGLObjects() == true ) { _deadTiles = new TileNodeRegistry("dead"); } // initialize the model factory: _tileModelFactory = new TileModelFactory(getMap(), _liveTiles.get(), _terrainOptions ); // handle an already-established map profile: if ( _update_mapf->getProfile() ) { // NOTE: this will initialize the map with the startup layers onMapInfoEstablished( MapInfo(map) ); } // populate the terrain with whatever data is in the map to begin with: if ( _terrain ) { // reserve a GPU image unit and two attribute indexes. this->getTextureCompositor()->reserveTextureImageUnit( _primaryUnit ); this->getTextureCompositor()->reserveTextureImageUnit( _secondaryUnit ); //this->getTextureCompositor()->reserveAttribIndex( _attribIndex1 ); //this->getTextureCompositor()->reserveAttribIndex( _attribIndex2 ); } // install a layer callback for processing further map actions: map->addMapCallback( new MPTerrainEngineNodeMapCallbackProxy(this) ); // Prime with existing layers: _batchUpdateInProgress = true; ElevationLayerVector elevationLayers; map->getElevationLayers( elevationLayers ); for( ElevationLayerVector::const_iterator i = elevationLayers.begin(); i != elevationLayers.end(); ++i ) addElevationLayer( i->get() ); ImageLayerVector imageLayers; map->getImageLayers( imageLayers ); for( ImageLayerVector::iterator i = imageLayers.begin(); i != imageLayers.end(); ++i ) addImageLayer( i->get() ); _batchUpdateInProgress = false; //{ // i->get()->addCallback( _elevationCallback.get() ); //} // install some terrain-wide uniforms this->getOrCreateStateSet()->getOrCreateUniform( "oe_min_tile_range_factor", osg::Uniform::FLOAT)->set( *_terrainOptions.minTileRangeFactor() ); // set up the initial shaders updateShaders(); // register this instance to the osgDB plugin can find it. registerEngine( this ); // now that we have a map, set up to recompute the bounds dirtyBound(); }
Config EarthFileSerializer2::serialize(const MapNode* input, const std::string& referrer) const { Config mapConf("map"); mapConf.set("version", "2"); if ( !input || !input->getMap() ) return mapConf; const Map* map = input->getMap(); MapFrame mapf( map, Map::ENTIRE_MODEL ); // the map and node options: Config optionsConf = map->getInitialMapOptions().getConfig(); optionsConf.merge( input->getMapNodeOptions().getConfig() ); mapConf.add( "options", optionsConf ); // the layers for( ImageLayerVector::const_iterator i = mapf.imageLayers().begin(); i != mapf.imageLayers().end(); ++i ) { ImageLayer* layer = i->get(); //Config layerConf = layer->getInitialOptions().getConfig(); Config layerConf = layer->getImageLayerOptions().getConfig(); layerConf.set("name", layer->getName()); layerConf.set("driver", layer->getInitialOptions().driver()->getDriver()); mapConf.add( "image", layerConf ); } for( ElevationLayerVector::const_iterator i = mapf.elevationLayers().begin(); i != mapf.elevationLayers().end(); ++i ) { ElevationLayer* layer = i->get(); //Config layerConf = layer->getInitialOptions().getConfig(); Config layerConf = layer->getElevationLayerOptions().getConfig(); layerConf.set("name", layer->getName()); layerConf.set("driver", layer->getInitialOptions().driver()->getDriver()); mapConf.add( "elevation", layerConf ); } for( ModelLayerVector::const_iterator i = mapf.modelLayers().begin(); i != mapf.modelLayers().end(); ++i ) { ModelLayer* layer = i->get(); Config layerConf = layer->getModelLayerOptions().getConfig(); layerConf.set("name", layer->getName()); layerConf.set("driver", layer->getModelLayerOptions().driver()->getDriver()); mapConf.add( "model", layerConf ); } Config ext = input->externalConfig(); if ( !ext.empty() ) { ext.key() = "extensions"; mapConf.add( ext ); } #if 1 // removed until it can be debugged. // Re-write pathnames in the Config so they are relative to the new referrer. if ( _rewritePaths && !referrer.empty() ) { RewritePaths rewritePaths( referrer ); rewritePaths.setRewriteAbsolutePaths( _rewriteAbsolutePaths ); rewritePaths.apply( mapConf ); } #endif return mapConf; }
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"); }
bool ElevationLayerVector::createHeightField(const TileKey& key, bool fallback, const Profile* haeProfile, ElevationInterpolation interpolation, ElevationSamplePolicy samplePolicy, osg::ref_ptr<osg::HeightField>& out_result, bool* out_isFallback, ProgressCallback* progress ) const { unsigned lowestLOD = key.getLevelOfDetail(); bool hfInitialized = false; //Get a HeightField for each of the enabled layers GeoHeightFieldVector heightFields; //The number of fallback heightfields we have int numFallbacks = 0; //Default to being fallback data. if ( out_isFallback ) { *out_isFallback = true; } // if the caller provided an "HAE map profile", he wants an HAE elevation grid even if // the map profile has a vertical datum. This is the usual case when building the 3D // terrain, for example. Construct a temporary key that doesn't have the vertical // datum info and use that to query the elevation data. TileKey keyToUse = key; if ( haeProfile ) { keyToUse = TileKey(key.getLevelOfDetail(), key.getTileX(), key.getTileY(), haeProfile ); } // Generate a heightfield for each elevation layer. unsigned defElevSize = 8; for( ElevationLayerVector::const_iterator i = this->begin(); i != this->end(); i++ ) { ElevationLayer* layer = i->get(); if ( layer->getVisible() ) { GeoHeightField geoHF = layer->createHeightField( keyToUse, progress ); // if "fallback" is set, try to fall back on lower LODs. if ( !geoHF.valid() && fallback ) { TileKey hf_key = keyToUse.createParentKey(); while ( hf_key.valid() && !geoHF.valid() ) { geoHF = layer->createHeightField( hf_key, progress ); if ( !geoHF.valid() ) hf_key = hf_key.createParentKey(); } if ( geoHF.valid() ) { if ( hf_key.getLevelOfDetail() < lowestLOD ) lowestLOD = hf_key.getLevelOfDetail(); //This HeightField is fallback data, so increment the count. numFallbacks++; } } if ( geoHF.valid() ) { heightFields.push_back( geoHF ); } } } //If any of the layers produced valid data then it's not considered a fallback if ( out_isFallback ) { *out_isFallback = (numFallbacks == heightFields.size()); //OE_NOTICE << "Num fallbacks=" << numFallbacks << " numHeightFields=" << heightFields.size() << " is fallback " << *out_isFallback << std::endl; } if ( heightFields.size() == 0 ) { //If we got no heightfields but were requested to fallback, create an empty heightfield. if ( fallback ) { out_result = HeightFieldUtils::createReferenceHeightField( keyToUse.getExtent(), defElevSize, defElevSize ); return true; } else { //We weren't requested to fallback so just return. return false; } } else if (heightFields.size() == 1) { if ( lowestLOD == key.getLevelOfDetail() ) { //If we only have on heightfield, just return it. out_result = heightFields[0].takeHeightField(); } else { GeoHeightField geoHF = heightFields[0].createSubSample( key.getExtent(), interpolation); out_result = geoHF.takeHeightField(); hfInitialized = true; } } else { //If we have multiple heightfields, we need to composite them together. unsigned int width = 0; unsigned int height = 0; for (GeoHeightFieldVector::const_iterator i = heightFields.begin(); i < heightFields.end(); ++i) { if (i->getHeightField()->getNumColumns() > width) width = i->getHeightField()->getNumColumns(); if (i->getHeightField()->getNumRows() > height) height = i->getHeightField()->getNumRows(); } out_result = new osg::HeightField(); out_result->allocate( width, height ); //Go ahead and 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); double dx = (maxx - minx)/(double)(out_result->getNumColumns()-1); double dy = (maxy - miny)/(double)(out_result->getNumRows()-1); const SpatialReference* keySRS = keyToUse.getProfile()->getSRS(); //Create the new heightfield by sampling all of them. for (unsigned int c = 0; c < width; ++c) { double x = minx + (dx * (double)c); for (unsigned r = 0; r < height; ++r) { double y = miny + (dy * (double)r); //Collect elevations from all of the layers. Iterate BACKWARDS because the last layer // is the highest priority. std::vector<float> elevations; for( GeoHeightFieldVector::reverse_iterator itr = heightFields.rbegin(); itr != heightFields.rend(); ++itr ) { const GeoHeightField& geoHF = *itr; float elevation = 0.0f; if ( geoHF.getElevation(keySRS, x, y, interpolation, keySRS, elevation) ) { if (elevation != NO_DATA_VALUE) { elevations.push_back(elevation); } } } float elevation = NO_DATA_VALUE; //The list of elevations only contains valid values if (elevations.size() > 0) { if (samplePolicy == SAMPLE_FIRST_VALID) { elevation = elevations[0]; } else if (samplePolicy == SAMPLE_HIGHEST) { elevation = -FLT_MAX; for (unsigned int i = 0; i < elevations.size(); ++i) { if (elevation < elevations[i]) elevation = elevations[i]; } } else if (samplePolicy == SAMPLE_LOWEST) { elevation = FLT_MAX; for (unsigned i = 0; i < elevations.size(); ++i) { if (elevation > elevations[i]) elevation = elevations[i]; } } else if (samplePolicy == SAMPLE_AVERAGE) { elevation = 0.0; for (unsigned i = 0; i < elevations.size(); ++i) { elevation += elevations[i]; } elevation /= (float)elevations.size(); } } out_result->setHeight(c, r, elevation); } } } // Replace any NoData areas with the reference value. This is zero for HAE datums, // and some geoid height for orthometric datums. if (out_result.valid()) { const Geoid* geoid = 0L; const VerticalDatum* vdatum = key.getProfile()->getSRS()->getVerticalDatum(); if ( haeProfile && vdatum ) { geoid = vdatum->getGeoid(); } HeightFieldUtils::resolveInvalidHeights( out_result.get(), key.getExtent(), NO_DATA_VALUE, geoid ); //ReplaceInvalidDataOperator o; //o.setValidDataOperator(new osgTerrain::NoDataValue(NO_DATA_VALUE)); //o( out_result.get() ); } //Initialize the HF values for osgTerrain if (out_result.valid() && !hfInitialized ) { //Go ahead and 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); out_result->setOrigin( osg::Vec3d( minx, miny, 0.0 ) ); double dx = (maxx - minx)/(double)(out_result->getNumColumns()-1); double dy = (maxy - miny)/(double)(out_result->getNumRows()-1); out_result->setXInterval( dx ); out_result->setYInterval( dy ); out_result->setBorderWidth( 0 ); } return out_result.valid(); }
void MPTerrainEngineNode::postInitialize( const Map* map, const TerrainOptions& options ) { TerrainEngineNode::postInitialize( map, options ); // Initialize the map frames. We need one for the update thread and one for the // cull thread. Someday we can detect whether these are actually the same thread // (depends on the viewer's threading mode). _update_mapf = new MapFrame( map, Map::ENTIRE_MODEL, "mp-update" ); // merge in the custom options: _terrainOptions.merge( options ); // A shared registry for tile nodes in the scene graph. Enable revision tracking // if requested in the options. Revision tracking lets the registry notify all // live tiles of the current map revision so they can inrementally update // themselves if necessary. _liveTiles = new TileNodeRegistry("live"); _liveTiles->setRevisioningEnabled( _terrainOptions.incrementalUpdate() == true ); _liveTiles->setMapRevision( _update_mapf->getRevision() ); // set up a registry for quick release: if ( _terrainOptions.quickReleaseGLObjects() == true ) { _deadTiles = new TileNodeRegistry("dead"); } // reserve GPU resources. Must do this before initializing the model factory. if ( _primaryUnit < 0 ) { getResources()->reserveTextureImageUnit( _primaryUnit, "MP Engine Primary" ); } // "Secondary" unit serves double duty; it's used for parent textures BUT it's also // used at the "slot" for the tile coordinates. if ( _secondaryUnit < 0 ) { getResources()->reserveTextureImageUnit( _secondaryUnit, "MP Engine Secondary" ); } // initialize the model factory: _tileModelFactory = new TileModelFactory(_liveTiles.get(), _terrainOptions, this); // handle an already-established map profile: if ( _update_mapf->getProfile() ) { // NOTE: this will initialize the map with the startup layers onMapInfoEstablished( MapInfo(map) ); } // install a layer callback for processing further map actions: map->addMapCallback( new MPTerrainEngineNodeMapCallbackProxy(this) ); // Prime with existing layers: _batchUpdateInProgress = true; ElevationLayerVector elevationLayers; map->getElevationLayers( elevationLayers ); for( ElevationLayerVector::const_iterator i = elevationLayers.begin(); i != elevationLayers.end(); ++i ) addElevationLayer( i->get() ); ImageLayerVector imageLayers; map->getImageLayers( imageLayers ); for( ImageLayerVector::iterator i = imageLayers.begin(); i != imageLayers.end(); ++i ) addImageLayer( i->get() ); _batchUpdateInProgress = false; // register this instance to the osgDB plugin can find it. registerEngine( this ); // set up the initial shaders and reserve the texture image units. updateState(); // now that we have a map, set up to recompute the bounds dirtyBound(); OE_INFO << LC << "Edge normalization is " << (_terrainOptions.normalizeEdges() == true? "ON" : "OFF") << std::endl; }
void RexTerrainEngineNode::postInitialize( const Map* map, const TerrainOptions& options ) { TerrainEngineNode::postInitialize( map, options ); // Initialize the map frames. We need one for the update thread and one for the // cull thread. Someday we can detect whether these are actually the same thread // (depends on the viewer's threading mode). _update_mapf = new MapFrame( map, Map::ENTIRE_MODEL ); // merge in the custom options: _terrainOptions.merge( options ); // morphing imagery LODs requires we bind parent textures to their own unit. if ( _terrainOptions.morphImagery() == true ) { _requireParentTextures = true; } // if the envvar for tile expiration is set, overide the options setting const char* val = ::getenv("OSGEARTH_EXPIRATION_THRESHOLD"); if ( val ) { _terrainOptions.expirationThreshold() = as<unsigned>(val, _terrainOptions.expirationThreshold().get()); OE_INFO << LC << "Expiration threshold set by env var = " << _terrainOptions.expirationThreshold().get() << "\n"; } // if the envvar for hires prioritization is set, override the options setting const char* hiresFirst = ::getenv("OSGEARTH_HIGH_RES_FIRST"); if ( hiresFirst ) { _terrainOptions.highResolutionFirst() = true; } // check for normal map generation (required for lighting). if ( _terrainOptions.normalMaps() == true ) { this->_requireNormalTextures = true; } // A shared registry for tile nodes in the scene graph. Enable revision tracking // if requested in the options. Revision tracking lets the registry notify all // live tiles of the current map revision so they can inrementally update // themselves if necessary. _liveTiles = new TileNodeRegistry("live"); _liveTiles->setMapRevision( _update_mapf->getRevision() ); if ( _terrainOptions.quickReleaseGLObjects() == true ) { _deadTiles = new TileNodeRegistry("dead"); _quickReleaseInstalled = false; ADJUST_UPDATE_TRAV_COUNT( this, +1 ); } // A shared geometry pool. if ( ::getenv("OSGEARTH_REX_NO_POOL") == 0L ) { _geometryPool = new GeometryPool( _terrainOptions ); } // Make a tile loader PagerLoader* loader = new PagerLoader( this ); loader->setMergesPerFrame( _terrainOptions.mergesPerFrame().get() ); _loader = loader; //_loader = new SimpleLoader(); this->addChild( _loader.get() ); // handle an already-established map profile: MapInfo mapInfo( map ); if ( _update_mapf->getProfile() ) { // NOTE: this will initialize the map with the startup layers onMapInfoEstablished( mapInfo ); } // install a layer callback for processing further map actions: map->addMapCallback( new RexTerrainEngineNodeMapCallbackProxy(this) ); // Prime with existing layers: _batchUpdateInProgress = true; ElevationLayerVector elevationLayers; map->getElevationLayers( elevationLayers ); for( ElevationLayerVector::const_iterator i = elevationLayers.begin(); i != elevationLayers.end(); ++i ) addElevationLayer( i->get() ); ImageLayerVector imageLayers; map->getImageLayers( imageLayers ); for( ImageLayerVector::iterator i = imageLayers.begin(); i != imageLayers.end(); ++i ) addImageLayer( i->get() ); _batchUpdateInProgress = false; // set up the initial shaders updateState(); // register this instance to the osgDB plugin can find it. registerEngine( this ); // now that we have a map, set up to recompute the bounds dirtyBound(); }
int purge( osg::ArgumentParser& args ) { osg::ref_ptr<osg::Node> node = osgDB::readNodeFiles( args ); if ( !node.valid() ) return usage( "Failed to read .earth file." ); MapNode* mapNode = MapNode::findMapNode( node.get() ); if ( !mapNode ) return usage( "Input file was not a .earth file" ); Map* map = mapNode->getMap(); if ( !map->getCache() ) return message( "Earth file does not contain a cache." ); std::vector<Entry> entries; ImageLayerVector imageLayers; map->getLayers( imageLayers ); for( ImageLayerVector::const_iterator i = imageLayers.begin(); i != imageLayers.end(); ++i ) { ImageLayer* layer = i->get(); bool useMFP = layer->getProfile() && layer->getProfile()->getSRS()->isSphericalMercator() && mapNode->getMapNodeOptions().getTerrainOptions().enableMercatorFastPath() == true; const Profile* cacheProfile = useMFP ? layer->getProfile() : map->getProfile(); CacheSettings* cacheSettings = layer->getCacheSettings(); if (cacheSettings) { CacheBin* bin = cacheSettings->getCacheBin(); if ( bin ) { entries.push_back(Entry()); entries.back()._isImage = true; entries.back()._name = i->get()->getName(); entries.back()._bin = bin; } } } ElevationLayerVector elevationLayers; map->getLayers( elevationLayers ); for( ElevationLayerVector::const_iterator i = elevationLayers.begin(); i != elevationLayers.end(); ++i ) { ElevationLayer* layer = i->get(); bool useMFP = layer->getProfile() && layer->getProfile()->getSRS()->isSphericalMercator() && mapNode->getMapNodeOptions().getTerrainOptions().enableMercatorFastPath() == true; const Profile* cacheProfile = useMFP ? layer->getProfile() : map->getProfile(); CacheSettings* cacheSettings = layer->getCacheSettings(); if (cacheSettings) { CacheBin* bin = cacheSettings->getCacheBin(); if (bin) { entries.push_back(Entry()); entries.back()._isImage = false; entries.back()._name = i->get()->getName(); entries.back()._bin = bin; } } } if ( entries.size() > 0 ) { std::cout << std::endl; for( unsigned i=0; i<entries.size(); ++i ) { std::cout << (i+1) << ") " << entries[i]._name << " (" << (entries[i]._isImage? "image" : "elevation" ) << ")" << std::endl; } std::cout << std::endl << "Enter number of cache to purge, or <enter> to quit: " << std::flush; std::string input; std::getline( std::cin, input ); if ( !input.empty() ) { unsigned k = as<unsigned>(input, 0L); if ( k > 0 && k <= entries.size() ) { Config meta = entries[k-1]._bin->readMetadata(); if ( !meta.empty() ) { std::cout << std::endl << "Cache METADATA:" << std::endl << meta.toJSON() << std::endl << std::endl; } std::cout << "Are you sure (y/N)? " << std::flush; std::getline( std::cin, input ); if ( input == "y" || input == "Y" ) { std::cout << "Purging.." << std::flush; entries[k-1]._bin->clear(); } else { std::cout << "No action taken." << std::endl; } } else { std::cout << "Invalid choice." << std::endl; } } else { std::cout << "No action taken." << std::endl; } } return 0; }
unsigned ElevationQuery::getMaxLevel( double x, double y, const SpatialReference* srs, const Profile* profile ) const { int targetTileSizePOT = nextPowerOf2((int)_mapf.getMapOptions().elevationTileSize().get()); int maxLevel = 0; for( ElevationLayerVector::const_iterator i = _mapf.elevationLayers().begin(); i != _mapf.elevationLayers().end(); ++i ) { const ElevationLayer* layer = i->get(); // skip disabled layers if ( !layer->getEnabled() || !layer->getVisible() ) continue; int layerMaxLevel = 0; osgEarth::TileSource* ts = layer->getTileSource(); if ( ts ) { // TileSource is good; check for optional data extents: if ( ts->getDataExtents().size() > 0 ) { osg::Vec3d tsCoord(x, y, 0); const SpatialReference* tsSRS = ts->getProfile() ? ts->getProfile()->getSRS() : 0L; if ( srs && tsSRS ) srs->transform(tsCoord, tsSRS, tsCoord); else tsSRS = srs; for (osgEarth::DataExtentList::iterator j = ts->getDataExtents().begin(); j != ts->getDataExtents().end(); j++) { if (j->maxLevel().isSet() && j->maxLevel() > layerMaxLevel && j->contains( tsCoord.x(), tsCoord.y(), tsSRS )) { layerMaxLevel = j->maxLevel().value(); } } } else { // Just use the default max level. Without any data extents we don't know the actual max layerMaxLevel = (int)(*layer->getTerrainLayerRuntimeOptions().maxLevel()); } // cap the max to the layer's express max level (if set). if ( layer->getTerrainLayerRuntimeOptions().maxLevel().isSet() ) { layerMaxLevel = std::min( layerMaxLevel, (int)(*layer->getTerrainLayerRuntimeOptions().maxLevel()) ); } // Need to convert the layer max of this TileSource to that of the actual profile layerMaxLevel = profile->getEquivalentLOD( ts->getProfile(), layerMaxLevel ); } else { // no TileSource? probably in cache-only mode. Use the layer max (or its default). layerMaxLevel = (int)(layer->getTerrainLayerRuntimeOptions().maxLevel().value()); } // Adjust for the tile size resolution differential, if supported by the layer. int layerTileSize = layer->getTileSize(); if (layerTileSize > targetTileSizePOT) { int oldMaxLevel = layerMaxLevel; int temp = std::max(targetTileSizePOT, 2); while(temp < layerTileSize) { temp *= 2; ++layerMaxLevel; } } if (layerMaxLevel > maxLevel) { maxLevel = layerMaxLevel; } } return maxLevel; }
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"); }
unsigned int ElevationQuery::getMaxLevel( double x, double y, const SpatialReference* srs, const Profile* profile ) const { unsigned int maxLevel = 0; for( ElevationLayerVector::const_iterator i = _mapf.elevationLayers().begin(); i != _mapf.elevationLayers().end(); ++i ) { unsigned int layerMax = 0; osgEarth::TileSource* ts = i->get()->getTileSource(); if ( ts && ts->getDataExtents().size() > 0 ) { osg::Vec3d tsCoord(x, y, 0); const SpatialReference* tsSRS = ts->getProfile() ? ts->getProfile()->getSRS() : 0L; if ( srs && tsSRS ) srs->transform(tsCoord, tsSRS, tsCoord); else tsSRS = srs; for (osgEarth::DataExtentList::iterator j = ts->getDataExtents().begin(); j != ts->getDataExtents().end(); j++) { if (j->maxLevel().isSet() && j->maxLevel() > layerMax && j->contains( tsCoord.x(), tsCoord.y(), tsSRS )) { layerMax = j->maxLevel().value(); } } //Need to convert the layer max of this TileSource to that of the actual profile layerMax = profile->getEquivalentLOD( ts->getProfile(), layerMax ); } else { layerMax = i->get()->getMaxDataLevel(); } if ( i->get()->getTerrainLayerRuntimeOptions().maxLevel().isSet() ) layerMax = std::min( layerMax, *i->get()->getTerrainLayerRuntimeOptions().maxLevel() ); if (layerMax > maxLevel) maxLevel = layerMax; } // need to check the image layers too, because if image layers do deeper than elevation layers, // upsampling occurs that can change the formation of the terrain skin. // NOTE: this probably doesn't happen in "triangulation" interpolation mode.. -gw for( ImageLayerVector::const_iterator i = _mapf.imageLayers().begin(); i != _mapf.imageLayers().end(); ++i ) { unsigned int layerMax = 0; osgEarth::TileSource* ts = i->get()->getTileSource(); if ( ts && ts->getDataExtents().size() > 0 ) { osg::Vec3d tsCoord(x, y, 0); const SpatialReference* tsSRS = ts->getProfile() ? ts->getProfile()->getSRS() : 0L; if ( srs && tsSRS ) srs->transform(tsCoord, tsSRS, tsCoord); else tsSRS = srs; for (osgEarth::DataExtentList::iterator j = ts->getDataExtents().begin(); j != ts->getDataExtents().end(); j++) { if (j->maxLevel().isSet() && j->maxLevel() > layerMax && j->contains( tsCoord.x(), tsCoord.y(), tsSRS )) { layerMax = j->maxLevel().value(); } } //Need to convert the layer max of this TileSource to that of the actual profile layerMax = profile->getEquivalentLOD( ts->getProfile(), layerMax ); } else { layerMax = i->get()->getMaxDataLevel(); } if ( i->get()->getTerrainLayerRuntimeOptions().maxLevel().isSet() ) layerMax = std::min( layerMax, *i->get()->getTerrainLayerRuntimeOptions().maxLevel() ); if (layerMax > maxLevel) maxLevel = layerMax; } return maxLevel; }
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"); }
int ElevationQuery::getMaxLevel( double x, double y, const SpatialReference* srs, const Profile* profile, unsigned tileSize) const { int targetTileSizePOT = nextPowerOf2((int)tileSize); int maxLevel = -1; for( ElevationLayerVector::const_iterator i = _mapf.elevationLayers().begin(); i != _mapf.elevationLayers().end(); ++i ) { const ElevationLayer* layer = i->get(); // skip disabled layers if ( !layer->getEnabled() || !layer->getVisible() ) continue; optional<int> layerMaxLevel; osgEarth::TileSource* ts = layer->getTileSource(); if ( ts ) { // TileSource is good; check for optional data extents: if ( ts->getDataExtents().size() > 0 ) { osg::Vec3d tsCoord(x, y, 0); const SpatialReference* tsSRS = ts->getProfile() ? ts->getProfile()->getSRS() : 0L; if ( srs && tsSRS ) srs->transform(tsCoord, tsSRS, tsCoord); else tsSRS = srs; for (osgEarth::DataExtentList::iterator j = ts->getDataExtents().begin(); j != ts->getDataExtents().end(); j++) { if (j->maxLevel().isSet() && (!layerMaxLevel.isSet() || j->maxLevel() > layerMaxLevel.get() ) && j->contains( tsCoord.x(), tsCoord.y(), tsSRS )) { layerMaxLevel = j->maxLevel().value(); } } } else { // Just use the default max level. Without any data extents we don't know the actual max layerMaxLevel = (int)(*layer->getTerrainLayerRuntimeOptions().maxLevel()); } // cap the max to the layer's express max level (if set). if ( layerMaxLevel.isSet() && layer->getTerrainLayerRuntimeOptions().maxLevel().isSet() ) { layerMaxLevel = std::min( layerMaxLevel.get(), (int)(*layer->getTerrainLayerRuntimeOptions().maxLevel()) ); } // Need to convert the layer max of this TileSource to that of the actual profile if ( layerMaxLevel.isSet() ) { layerMaxLevel = profile->getEquivalentLOD( ts->getProfile(), layerMaxLevel.get() ); } } else { // no TileSource? probably in cache-only mode. Use the layer max (or its default). layerMaxLevel = (int)(layer->getTerrainLayerRuntimeOptions().maxLevel().value()); } // Adjust for the tile size resolution differential, if supported by the layer. if ( layerMaxLevel.isSet() ) { // TODO: This native max resolution of the layer has already been computed here. // The following block attempts to compute a higher resolution to undo the resolution // mapping that populateHeightField will eventually do. So for example, you might compute a maximum level of // 10 here, and this will adjust it to 14 with the knowledge that populateHeightField will adjust the 14 back to 10. // The use of populateHeightField needs to be replaced by code that just works with the native resolution of the // layers instead. #if 1 int layerTileSize = layer->getTileSize(); if (layerTileSize > targetTileSizePOT) { int temp = std::max(targetTileSizePOT, 2); while(temp < layerTileSize) { temp *= 2; layerMaxLevel = layerMaxLevel.get() + 1; } } #endif if (layerMaxLevel > maxLevel) { maxLevel = layerMaxLevel.get(); } } } return maxLevel; }