void Map::clear() { ImageLayerVector imageLayersRemoved; ElevationLayerVector elevLayersRemoved; ModelLayerVector modelLayersRemoved; MaskLayerVector maskLayersRemoved; Revision newRevision; { Threading::ScopedWriteLock lock( _mapDataMutex ); imageLayersRemoved.swap( _imageLayers ); elevLayersRemoved.swap ( _elevationLayers ); modelLayersRemoved.swap( _modelLayers ); // calculate a new revision. newRevision = ++_dataModelRevision; } // a separate block b/c we don't need the mutex for( MapCallbackList::iterator i = _mapCallbacks.begin(); i != _mapCallbacks.end(); i++ ) { for( ImageLayerVector::iterator k = imageLayersRemoved.begin(); k != imageLayersRemoved.end(); ++k ) i->get()->onMapModelChanged( MapModelChange(MapModelChange::REMOVE_IMAGE_LAYER, newRevision, k->get()) ); for( ElevationLayerVector::iterator k = elevLayersRemoved.begin(); k != elevLayersRemoved.end(); ++k ) i->get()->onMapModelChanged( MapModelChange(MapModelChange::REMOVE_ELEVATION_LAYER, newRevision, k->get()) ); for( ModelLayerVector::iterator k = modelLayersRemoved.begin(); k != modelLayersRemoved.end(); ++k ) i->get()->onMapModelChanged( MapModelChange(MapModelChange::REMOVE_MODEL_LAYER, newRevision, k->get()) ); } }
void GlobePlugin::elevationLayersChanged() { if ( mIsGlobeRunning ) { QgsDebugMsg( "elevationLayersChanged: Globe Running, executing" ); osg::ref_ptr<Map> map = mMapNode->getMap(); if ( map->getNumElevationLayers() > 1 ) { mOsgViewer->getDatabasePager()->clear(); } // Remove elevation layers ElevationLayerVector list; map->getElevationLayers( list ); for ( ElevationLayerVector::iterator i = list.begin(); i != list.end(); ++i ) { map->removeElevationLayer( *i ); } // Add elevation layers QSettings settings; QString cacheDirectory = settings.value( "cache/directory", QgsApplication::qgisSettingsDirPath() + "cache" ).toString(); QTableWidget* table = mSettingsDialog->elevationDatasources(); for ( int i = 0; i < table->rowCount(); ++i ) { QString type = table->item( i, 0 )->text(); //bool cache = table->item( i, 1 )->checkState(); QString uri = table->item( i, 2 )->text(); ElevationLayer* layer = 0; if ( "Raster" == type ) { GDALOptions options; options.url() = uri.toStdString(); layer = new osgEarth::ElevationLayer( uri.toStdString(), options ); } else if ( "TMS" == type ) { TMSOptions options; options.url() = uri.toStdString(); layer = new osgEarth::ElevationLayer( uri.toStdString(), options ); } map->addElevationLayer( layer ); //if ( !cache || type == "Worldwind" ) layer->setCache( 0 ); //no tms cache for worldwind (use worldwind_cache) } #if OSGEARTH_VERSION_GREATER_OR_EQUAL( 2, 5, 0 ) double scale = QgsProject::instance()->readDoubleEntry( "Globe-Plugin", "/verticalScale", 1 ); setVerticalScale( scale ); #endif } else { QgsDebugMsg( "layersChanged: Globe NOT running, skipping" ); return; } }
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(); }
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 onClick(Control*) { Map* map = s_mapNode->getMap(); ElevationLayerVector layers; map->getLayers(layers); map->beginUpdate(); for (ElevationLayerVector::iterator i = layers.begin(); i != layers.end(); ++i) { map->removeLayer(i->get()); } map->endUpdate(); }
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; }
void Map::setLayersFromMap( const Map* map ) { this->clear(); if ( map ) { ImageLayerVector newImages; map->getImageLayers( newImages ); for( ImageLayerVector::iterator i = newImages.begin(); i != newImages.end(); ++i ) addImageLayer( i->get() ); ElevationLayerVector newElev; map->getElevationLayers( newElev ); for( ElevationLayerVector::iterator i = newElev.begin(); i != newElev.end(); ++i ) addElevationLayer( i->get() ); ModelLayerVector newModels; map->getModelLayers( newModels ); for( ModelLayerVector::iterator i = newModels.begin(); i != newModels.end(); ++i ) addModelLayer( i->get() ); } }
bool ElevationPool::fetchTileFromMap(const TileKey& key, const ElevationLayerVector& layers, Tile* tile) { tile->_loadTime = osg::Timer::instance()->tick(); osg::ref_ptr<osg::HeightField> hf = new osg::HeightField(); hf->allocate( _tileSize, _tileSize ); // Initialize the heightfield to nodata hf->getFloatArray()->assign( hf->getFloatArray()->size(), NO_DATA_VALUE ); TileKey keyToUse = key; while( !tile->_hf.valid() && keyToUse.valid() ) { bool ok; if (_layers.empty()) { OE_TEST << LC << "Populating from envelope (" << keyToUse.str() << ")\n"; ok = layers.populateHeightFieldAndNormalMap(hf.get(), 0L, keyToUse, 0L, INTERP_BILINEAR, 0L); } else { OE_TEST << LC << "Populating from layers (" << keyToUse.str() << ")\n"; ok = _layers.populateHeightFieldAndNormalMap(hf.get(), 0L, keyToUse, 0L, INTERP_BILINEAR, 0L); } if (ok) { tile->_hf = GeoHeightField( hf.get(), keyToUse.getExtent() ); tile->_bounds = keyToUse.getExtent().bounds(); } else { keyToUse = keyToUse.createParentKey(); } } return tile->_hf.valid(); }
void updateControlPanel() { // erase all child controls and just rebuild them b/c we're lazy. //Rebuild all the image layers s_imageBox->clearControls(); int row = 0; LabelControl* activeLabel = new LabelControl( "Image Layers", 20, osg::Vec4f(1,1,0,1) ); s_imageBox->setControl( 1, row++, activeLabel ); // the active map layers: MapFrame mapf( s_activeMap.get() ); ImageLayerVector imageLayers; mapf.getLayers(imageLayers); int layerNum = imageLayers.size()-1; for( ImageLayerVector::const_reverse_iterator i = imageLayers.rbegin(); i != imageLayers.rend(); ++i ) createLayerItem( s_imageBox, row++, layerNum--, imageLayers.size(), i->get(), true ); MapFrame mapf2( s_inactiveMap.get() ); imageLayers.clear(); mapf2.getLayers(imageLayers); if ( imageLayers.size() > 0 ) { LabelControl* inactiveLabel = new LabelControl( "Removed:", 18, osg::Vec4f(1,1,0,1) ); s_imageBox->setControl( 1, row++, inactiveLabel ); for( unsigned int i=0; i<imageLayers.size(); ++i ) { createLayerItem( s_imageBox, row++, -1, -1, imageLayers[i].get(), false ); } } //Rebuild the elevation layers s_elevationBox->clearControls(); row = 0; activeLabel = new LabelControl( "Elevation Layers", 20, osg::Vec4f(1,1,0,1) ); s_elevationBox->setControl( 1, row++, activeLabel ); // the active map layers: ElevationLayerVector elevationLayers; mapf.getLayers(elevationLayers); layerNum = elevationLayers.size()-1; for( ElevationLayerVector::const_reverse_iterator i = elevationLayers.rbegin(); i != elevationLayers.rend(); ++i ) createLayerItem( s_elevationBox, row++, layerNum--, elevationLayers.size(), i->get(), true ); if ( mapf2.elevationLayers().size() > 0 ) { LabelControl* inactiveLabel = new LabelControl( "Removed:", 18, osg::Vec4f(1,1,0,1) ); s_elevationBox->setControl( 1, row++, inactiveLabel ); for( unsigned int i=0; i<mapf2.elevationLayers().size(); ++i ) { createLayerItem( s_elevationBox, row++, -1, -1, mapf2.elevationLayers().at(i), false ); } } //Rebuild the model layers s_modelBox->clearControls(); row = 0; activeLabel = new LabelControl( "Model Layers", 20, osg::Vec4f(1,1,0,1) ); s_modelBox->setControl( 1, row++, activeLabel ); // the active map layers: ModelLayerVector modelLayers; mapf.getLayers(modelLayers); for( ModelLayerVector::const_reverse_iterator i = modelLayers.rbegin(); i != modelLayers.rend(); ++i ) createModelLayerItem( s_modelBox, row++, i->get(), true ); }
void GlobePlugin::layersChanged() { if ( mIsGlobeRunning ) { QgsDebugMsg( "layersChanged: Globe Running, executing" ); osg::ref_ptr<Map> map = mMapNode->getMap(); if ( map->getNumImageLayers() > 1 || map->getNumElevationLayers() > 1 ) { viewer.getDatabasePager()->clear(); } // Remove elevation layers ElevationLayerVector list; map->getElevationLayers( list ); for ( ElevationLayerVector::iterator i = list.begin(); i != list.end(); i++ ) { map->removeElevationLayer( *i ); } // Add elevation layers QSettings settings; QString cacheDirectory = settings.value( "cache/directory", QgsApplication::qgisSettingsDirPath() + "cache" ).toString(); QTableWidget* table = mSettingsDialog.elevationDatasources(); for ( int i = 0; i < table->rowCount(); ++i ) { QString type = table->item( i, 0 )->text(); bool cache = table->item( i, 1 )->checkState(); QString uri = table->item( i, 2 )->text(); ElevationLayer* layer = 0; if ( "Raster" == type ) { GDALOptions options; options.url() = uri.toStdString(); layer = new osgEarth::ElevationLayer( uri.toStdString(), options ); } else if ( "Worldwind" == type ) { WorldWindOptions options; options.elevationCachePath() = cacheDirectory.toStdString() + "/globe/worldwind_srtm"; layer = new osgEarth::ElevationLayer( "WorldWind bil", options ); TerrainEngineNode* terrainEngineNode = mMapNode->getTerrainEngine(); terrainEngineNode->setVerticalScale( 2 ); terrainEngineNode->setElevationSamplingRatio( 0.25 ); } else if ( "TMS" == type ) { TMSOptions options; options.url() = uri.toStdString(); layer = new osgEarth::ElevationLayer( uri.toStdString(), options ); } map->addElevationLayer( layer ); if ( !cache || type == "Worldwind" ) layer->setCache( 0 ); //no tms cache for worldwind (use worldwind_cache) } //remove QGIS layer if ( mQgisMapLayer ) { QgsDebugMsg( "removeMapLayer" ); map->removeImageLayer( mQgisMapLayer ); } //add QGIS layer QgsDebugMsg( "addMapLayer" ); mTileSource = new QgsOsgEarthTileSource( mQGisIface ); mTileSource->initialize( "", 0 ); ImageLayerOptions options( "QGIS" ); mQgisMapLayer = new ImageLayer( options, mTileSource ); map->addImageLayer( mQgisMapLayer ); mQgisMapLayer->setCache( 0 ); //disable caching } else { QgsDebugMsg( "layersChanged: Globe NOT running, skipping" ); return; } }
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; }
bool ElevationLayerVector::populateHeightField(osg::HeightField* hf, const TileKey& key, const Profile* haeProfile, ElevationInterpolation interpolation, ProgressCallback* progress ) const { // heightfield must already exist. if ( !hf ) return false; // 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.getLOD(), key.getTileX(), key.getTileY(), haeProfile ); } // Collect the valid layers for this tile. ElevationLayerVector contenders; ElevationLayerVector offsets; for(ElevationLayerVector::const_reverse_iterator i = this->rbegin(); i != this->rend(); ++i) { ElevationLayer* layer = i->get(); if ( layer->getEnabled() && layer->getVisible() ) { // calculate the resolution-mapped key (adjusted for tile resolution differential). TileKey mappedKey = keyToUse.mapResolution(hf->getNumColumns(), layer->getTileSize()); // Note: isKeyInRange tests the key, but haData tests the mapped key. // I think that's right! if ((layer->getTileSource() == 0L) || (layer->isKeyInRange(key) && layer->getTileSource()->hasData(mappedKey))) { if (layer->isOffset()) offsets.push_back(layer); else contenders.push_back(layer); } } } // nothing? bail out. if ( contenders.empty() && offsets.empty() ) { return false; } // Sample the layers into our target. unsigned numColumns = hf->getNumColumns(); unsigned numRows = hf->getNumRows(); double xmin = key.getExtent().xMin(); double ymin = key.getExtent().yMin(); double dx = key.getExtent().width() / (double)(numColumns-1); double dy = key.getExtent().height() / (double)(numRows-1); // We will load the actual heightfields on demand. We might not need them all. GeoHeightFieldVector heightFields(contenders.size()); GeoHeightFieldVector offsetFields(offsets.size()); std::vector<bool> heightFailed (contenders.size(), false); std::vector<bool> offsetFailed(offsets.size(), false); const SpatialReference* keySRS = keyToUse.getProfile()->getSRS(); bool realData = false; for (unsigned c = 0; c < numColumns; ++c) { double x = xmin + (dx * (double)c); for (unsigned r = 0; r < numRows; ++r) { double y = ymin + (dy * (double)r); // Collect elevations from each layer as necessary. bool resolved = false; for(int i=0; i<contenders.size() && !resolved; ++i) { if ( heightFailed[i] ) continue; GeoHeightField& layerHF = heightFields[i]; if ( !layerHF.valid() ) { TileKey mappedKey = keyToUse.mapResolution(hf->getNumColumns(), contenders[i]->getTileSize()); layerHF = contenders[i]->createHeightField(mappedKey, progress); if ( !layerHF.valid() ) { heightFailed[i] = true; continue; } } // If we actually got a layer then we have real data realData = true; float elevation; if (layerHF.getElevation(keySRS, x, y, interpolation, keySRS, elevation) && elevation != NO_DATA_VALUE) { resolved = true; hf->setHeight(c, r, elevation); } } for(int i=offsets.size()-1; i>=0; --i) { if ( offsetFailed[i] ) continue; GeoHeightField& layerHF = offsetFields[i]; if ( !layerHF.valid() ) { TileKey mappedKey = keyToUse.mapResolution(hf->getNumColumns(), offsets[i]->getTileSize()); layerHF = offsets[i]->createHeightField(mappedKey, progress); if ( !layerHF.valid() ) { offsetFailed[i] = true; continue; } } // If we actually got a layer then we have real data realData = true; float elevation = 0.0f; if (layerHF.getElevation(keySRS, x, y, interpolation, keySRS, elevation) && elevation != NO_DATA_VALUE) { hf->getHeight(c, r) += elevation; } } } } // Return whether or not we actually read any real data return realData; }
/** Packages an image layer as a TMS folder. */ int makeTMS( osg::ArgumentParser& args ) { // see if the user wants to override the type extension (imagery only) std::string extension = "png"; args.read( "--ext", extension ); // verbosity? bool verbose = !args.read( "--quiet" ); // find a .earth file on the command line std::string earthFile = findArgumentWithExtension(args, ".earth"); if ( earthFile.empty() ) return usage( "Missing required .earth file" ); // folder to which to write the TMS archive. std::string rootFolder; if ( !args.read( "--out", rootFolder ) ) rootFolder = Stringify() << earthFile << ".tms_repo"; // max level to which to generate unsigned maxLevel = ~0; args.read( "--max-level", maxLevel ); // load up the map osg::ref_ptr<MapNode> mapNode = MapNode::load( args ); if ( !mapNode.valid() ) return usage( "Failed to load a valid .earth file" ); // create a folder for the output osgDB::makeDirectory(rootFolder); if ( !osgDB::fileExists(rootFolder) ) return usage("Failed to create root output folder" ); Map* map = mapNode->getMap(); // fire up a packager: TMSPackager packager( map->getProfile() ); packager.setVerbose( verbose ); if ( maxLevel != ~0 ) packager.setMaxLevel( maxLevel ); // package any image layers that are enabled: ImageLayerVector imageLayers; map->getImageLayers( imageLayers ); unsigned counter = 0; for( ImageLayerVector::iterator i = imageLayers.begin(); i != imageLayers.end(); ++i, ++counter ) { ImageLayer* layer = i->get(); if ( layer->getImageLayerOptions().enabled() == true ) { std::string layerFolder = toLegalFileName( layer->getName() ); if ( layerFolder.empty() ) layerFolder = Stringify() << "image_layer_" << counter; if ( verbose ) { OE_NOTICE << LC << "Packaging image layer \"" << layerFolder << "\"" << std::endl; } std::string layerRoot = osgDB::concatPaths( rootFolder, layerFolder ); TMSPackager::Result r = packager.package( layer, layerRoot, extension ); if ( !r.ok ) { OE_WARN << LC << r.message << std::endl; } } else if ( verbose ) { OE_NOTICE << LC << "Skipping disabled layer \"" << layer->getName() << "\"" << std::endl; } } // package any elevation layers that are enabled: counter = 0; ElevationLayerVector elevationLayers; map->getElevationLayers( elevationLayers ); for( ElevationLayerVector::iterator i = elevationLayers.begin(); i != elevationLayers.end(); ++i, ++counter ) { ElevationLayer* layer = i->get(); if ( layer->getElevationLayerOptions().enabled() == true ) { std::string layerFolder = toLegalFileName( layer->getName() ); if ( layerFolder.empty() ) layerFolder = Stringify() << "elevation_layer_" << counter; if ( verbose ) { OE_NOTICE << LC << "Packaging elevation layer \"" << layerFolder << "\"" << std::endl; } std::string layerRoot = osgDB::concatPaths( rootFolder, layerFolder ); packager.package( layer, layerRoot ); } else if ( verbose ) { OE_NOTICE << LC << "Skipping disabled layer \"" << layer->getName() << "\"" << std::endl; } } return 0; }
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(); }
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; }
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; }
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(); }
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(); }
bool TerrainTileModelFactory::getOrCreateHeightField(const Map* map, const TileKey& key, ElevationSamplePolicy samplePolicy, ElevationInterpolation interpolation, unsigned border, osg::ref_ptr<osg::HeightField>& out_hf, osg::ref_ptr<NormalMap>& out_normalMap, ProgressCallback* progress) { // check the quick cache. HFCacheKey cachekey; cachekey._key = key; cachekey._revision = map->getDataModelRevision(); cachekey._samplePolicy = samplePolicy; if (progress) progress->stats()["hfcache_try_count"] += 1; bool hit = false; HFCache::Record rec; if ( _heightFieldCacheEnabled && _heightFieldCache.get(cachekey, rec) ) { out_hf = rec.value()._hf.get(); out_normalMap = rec.value()._normalMap.get(); if (progress) { progress->stats()["hfcache_hit_count"] += 1; progress->stats()["hfcache_hit_rate"] = progress->stats()["hfcache_hit_count"]/progress->stats()["hfcache_try_count"]; } return true; } if ( !out_hf.valid() ) { out_hf = HeightFieldUtils::createReferenceHeightField( key.getExtent(), 257, 257, // base tile size for elevation data border, // 1 sample border around the data makes it 259x259 true); // initialize to HAE (0.0) heights } if (!out_normalMap.valid()) { //OE_INFO << "TODO: check terrain reqs\n"; out_normalMap = new NormalMap(257, 257); // ImageUtils::createEmptyImage(257, 257); } ElevationLayerVector layers; map->getLayers(layers); bool populated = layers.populateHeightFieldAndNormalMap( out_hf.get(), out_normalMap.get(), key, map->getProfileNoVDatum(), // convertToHAE, INTERP_BILINEAR, progress ); #ifdef TREAT_ALL_ZEROS_AS_MISSING_TILE // check for a real tile with all zeros and treat it the same as non-existant data. if ( populated ) { bool isEmpty = true; for(osg::FloatArray::const_iterator f = out_hf->getFloatArray()->begin(); f != out_hf->getFloatArray()->end(); ++f) { if ( (*f) != 0.0f ) { isEmpty = false; break; } } if ( isEmpty ) { populated = false; } } #endif if ( populated ) { // Treat Plate Carre specially by scaling the height values. (There is no need // to do this with an empty heightfield) if (map->getSRS()->isPlateCarre()) { HeightFieldUtils::scaleHeightFieldToDegrees( out_hf.get() ); } // cache it. if (_heightFieldCacheEnabled ) { HFCacheValue newValue; newValue._hf = out_hf.get(); newValue._normalMap = out_normalMap.get(); _heightFieldCache.insert( cachekey, newValue ); } } return populated; }
/** Packages an image layer as a TMS folder. */ int makeTMS( osg::ArgumentParser& args ) { // see if the user wants to override the type extension (imagery only) std::string extension; args.read( "--ext", extension ); // verbosity? bool verbose = !args.read( "--quiet" ); // find a .earth file on the command line std::string earthFile = findArgumentWithExtension( args, ".earth" ); /* if ( earthFile.empty() ) return usage( "Missing required .earth file" ); */ // folder to which to write the TMS archive. std::string rootFolder; if( !args.read( "--out", rootFolder ) ) rootFolder = Stringify() << earthFile << ".tms_repo"; // whether to overwrite existing tile files bool overwrite = false; if( args.read( "--overwrite" ) ) overwrite = true; // write out an earth file std::string outEarth; args.read( "--out-earth", outEarth ); std::string dbOptions; args.read( "--db-options", dbOptions ); std::string::size_type n = 0; while( (n = dbOptions.find( '"', n )) != dbOptions.npos ) { dbOptions.erase( n, 1 ); } osg::ref_ptr<osgDB::Options> options = new osgDB::Options( dbOptions ); std::vector< Bounds > bounds; // restrict packaging to user-specified bounds. double xmin = DBL_MAX, ymin = DBL_MAX, xmax = DBL_MIN, ymax = DBL_MIN; while( args.read( "--bounds", xmin, ymin, xmax, ymax ) ) { Bounds b; b.xMin() = xmin, b.yMin() = ymin, b.xMax() = xmax, b.yMax() = ymax; bounds.push_back( b ); } // max level to which to generate unsigned maxLevel = ~0; args.read( "--max-level", maxLevel ); // whether to keep 'empty' tiles bool keepEmpties = args.read( "--keep-empties" ); bool continueSingleColor = args.read( "--continue-single-color" ); // max level to which to generate unsigned elevationPixelDepth = 32; args.read( "--elevation-pixel-depth", elevationPixelDepth ); // load up the map osg::ref_ptr<MapNode> mapNode = MapNode::load( args ); if( !mapNode.valid() ) return usage( "Failed to load a valid .earth file" ); // create a folder for the output osgDB::makeDirectory( rootFolder ); if( !osgDB::fileExists( rootFolder ) ) return usage( "Failed to create root output folder" ); Map* map = mapNode->getMap(); // fire up a packager: TMSPackager packager( map->getProfile(), options ); packager.setVerbose( verbose ); packager.setOverwrite( overwrite ); packager.setKeepEmptyImageTiles( keepEmpties ); packager.setSubdivideSingleColorImageTiles( continueSingleColor ); packager.setElevationPixelDepth( elevationPixelDepth ); if( maxLevel != ~0 ) packager.setMaxLevel( maxLevel ); if( bounds.size() > 0 ) { for( unsigned int i = 0; i < bounds.size(); ++i ) { Bounds b = bounds[i]; if( b.isValid() ) packager.addExtent( GeoExtent( map->getProfile()->getSRS(), b ) ); } } // new map for an output earth file if necessary. osg::ref_ptr<Map> outMap = 0L; if( !outEarth.empty() ) { // copy the options from the source map first outMap = new Map( map->getInitialMapOptions() ); } // establish the output path of the earth file, if applicable: std::string outEarthFile = osgDB::concatPaths( rootFolder, osgDB::getSimpleFileName( outEarth ) ); // package any image layers that are enabled: ImageLayerVector imageLayers; map->getImageLayers( imageLayers ); unsigned counter = 0; for( ImageLayerVector::iterator i = imageLayers.begin(); i != imageLayers.end(); ++i, ++counter ) { ImageLayer* layer = i->get(); if( layer->getImageLayerOptions().enabled() == true ) { std::string layerFolder = toLegalFileName( layer->getName() ); if( layerFolder.empty() ) layerFolder = Stringify() << "image_layer_" << counter; if( verbose ) { OE_NOTICE << LC << "Packaging image layer \"" << layerFolder << "\"" << std::endl; } osg::ref_ptr< ConsoleProgressCallback > progress = new ConsoleProgressCallback(); std::string layerRoot = osgDB::concatPaths( rootFolder, layerFolder ); TMSPackager::Result r = packager.package( layer, layerRoot, progress, extension ); if( r.ok ) { // save to the output map if requested: if( outMap.valid() ) { // new TMS driver info: TMSOptions tms; tms.url() = URI( osgDB::concatPaths( layerFolder, "tms.xml" ), outEarthFile ); ImageLayerOptions layerOptions( layer->getName(), tms ); layerOptions.mergeConfig( layer->getInitialOptions().getConfig( true ) ); layerOptions.cachePolicy() = CachePolicy::NO_CACHE; outMap->addImageLayer( new ImageLayer( layerOptions ) ); } } else { OE_WARN << LC << r.message << std::endl; } } else if( verbose ) { OE_NOTICE << LC << "Skipping disabled layer \"" << layer->getName() << "\"" << std::endl; } } // package any elevation layers that are enabled: counter = 0; ElevationLayerVector elevationLayers; map->getElevationLayers( elevationLayers ); for( ElevationLayerVector::iterator i = elevationLayers.begin(); i != elevationLayers.end(); ++i, ++counter ) { ElevationLayer* layer = i->get(); if( layer->getElevationLayerOptions().enabled() == true ) { std::string layerFolder = toLegalFileName( layer->getName() ); if( layerFolder.empty() ) layerFolder = Stringify() << "elevation_layer_" << counter; if( verbose ) { OE_NOTICE << LC << "Packaging elevation layer \"" << layerFolder << "\"" << std::endl; } std::string layerRoot = osgDB::concatPaths( rootFolder, layerFolder ); TMSPackager::Result r = packager.package( layer, layerRoot ); if( r.ok ) { // save to the output map if requested: if( outMap.valid() ) { // new TMS driver info: TMSOptions tms; tms.url() = URI( osgDB::concatPaths( layerFolder, "tms.xml" ), outEarthFile ); ElevationLayerOptions layerOptions( layer->getName(), tms ); layerOptions.mergeConfig( layer->getInitialOptions().getConfig( true ) ); layerOptions.cachePolicy() = CachePolicy::NO_CACHE; outMap->addElevationLayer( new ElevationLayer( layerOptions ) ); } } else { OE_WARN << LC << r.message << std::endl; } } else if( verbose ) { OE_NOTICE << LC << "Skipping disabled layer \"" << layer->getName() << "\"" << std::endl; } } // Finally, write an earth file if requested: if( outMap.valid() ) { MapNodeOptions outNodeOptions = mapNode->getMapNodeOptions(); osg::ref_ptr<MapNode> outMapNode = new MapNode( outMap.get(), outNodeOptions ); if( !osgDB::writeNodeFile( *outMapNode.get(), outEarthFile ) ) { OE_WARN << LC << "Error writing earth file to \"" << outEarthFile << "\"" << std::endl; } else if( verbose ) { OE_NOTICE << LC << "Wrote earth file to \"" << outEarthFile << "\"" << std::endl; } } return 0; }
/** Packages image and elevation layers as a TMS. */ int TMSExporter::exportTMS(MapNode* mapNode, const std::string& path, std::vector< osgEarth::Bounds >& bounds, const std::string& outEarth, bool overwrite, const std::string& extension) { if ( !mapNode ) { _errorMessage = "Invalid MapNode"; if (_progress.valid()) _progress->onCompleted(); return 0; } // folder to which to write the TMS archive. std::string rootFolder = path; osg::ref_ptr<osgDB::Options> options = new osgDB::Options(_dbOptions); // create a folder for the output osgDB::makeDirectory(rootFolder); if ( !osgDB::fileExists(rootFolder) ) { _errorMessage = "Failed to create root output folder"; if (_progress.valid()) _progress->onCompleted(); return 0; } Map* map = mapNode->getMap(); // new map for an output earth file if necessary. osg::ref_ptr<Map> outMap = 0L; if ( !outEarth.empty() ) { // copy the options from the source map first outMap = new Map(map->getInitialMapOptions()); } // establish the output path of the earth file, if applicable: std::string outEarthName = osgDB::getSimpleFileName(outEarth); if (outEarthName.length() > 0 && osgEarth::toLower(osgDB::getFileExtension(outEarthName)) != "earth") outEarthName += ".earth"; std::string outEarthFile = osgDB::concatPaths(rootFolder, outEarthName); // semaphore and tasks collection for multithreading osgEarth::Threading::MultiEvent semaphore; osgEarth::TaskRequestVector tasks; int taskCount = 0; // package any image layers that are enabled and visible ImageLayerVector imageLayers; map->getImageLayers( imageLayers ); unsigned imageCount = 0; for( ImageLayerVector::iterator i = imageLayers.begin(); i != imageLayers.end(); ++i, ++imageCount ) { ImageLayer* layer = i->get(); if ( layer->getEnabled() && layer->getVisible() ) { std::string layerFolder = toLegalFileName( layer->getName() ); if ( layerFolder.empty() ) layerFolder = Stringify() << "image_layer_" << imageCount; ParallelTask<PackageLayer>* task = new ParallelTask<PackageLayer>( &semaphore ); task->init(map, layer, options, rootFolder, layerFolder, true, overwrite, _keepEmpties, _maxLevel, extension, bounds); task->setProgressCallback(new PackageLayerProgressCallback(this)); tasks.push_back(task); taskCount++; } } // package any elevation layers that are enabled and visible ElevationLayerVector elevationLayers; map->getElevationLayers( elevationLayers ); int elevCount = 0; for( ElevationLayerVector::iterator i = elevationLayers.begin(); i != elevationLayers.end(); ++i, ++elevCount ) { ElevationLayer* layer = i->get(); if ( layer->getEnabled() && layer->getVisible() ) { std::string layerFolder = toLegalFileName( layer->getName() ); if ( layerFolder.empty() ) layerFolder = Stringify() << "elevation_layer_" << elevCount; ParallelTask<PackageLayer>* task = new ParallelTask<PackageLayer>( &semaphore ); task->init(map, layer, options, rootFolder, layerFolder, true, overwrite, _keepEmpties, _maxLevel, extension, bounds); task->setProgressCallback(new PackageLayerProgressCallback(this)); tasks.push_back(task); taskCount++; } } // Run all the tasks in parallel _totalTasks = taskCount; _completedTasks = 0; semaphore.reset( _totalTasks ); for( TaskRequestVector::iterator i = tasks.begin(); i != tasks.end(); ++i ) _taskService->add( i->get() ); // Wait for them to complete semaphore.wait(); // Add successfully packaged layers to the new map object and // write out the .earth file (if requested) if (outMap.valid()) { for( TaskRequestVector::iterator i = tasks.begin(); i != tasks.end(); ++i ) { PackageLayer* p = dynamic_cast<PackageLayer*>(i->get()); if (p) { if (p->_packageResult.ok) { TMSOptions tms; tms.url() = URI(osgDB::concatPaths(p->_layerFolder, "tms.xml"), outEarthFile ); if (p->_imageLayer.valid()) { ImageLayerOptions layerOptions( p->_imageLayer->getName(), tms ); layerOptions.mergeConfig( p->_imageLayer->getInitialOptions().getConfig(true) ); layerOptions.cachePolicy() = CachePolicy::NO_CACHE; outMap->addImageLayer( new ImageLayer(layerOptions) ); } else { ElevationLayerOptions layerOptions( p->_elevationLayer->getName(), tms ); layerOptions.mergeConfig( p->_elevationLayer->getInitialOptions().getConfig(true) ); layerOptions.cachePolicy() = CachePolicy::NO_CACHE; outMap->addElevationLayer( new ElevationLayer(layerOptions) ); } } else { OE_WARN << LC << p->_packageResult.message << std::endl; } } } } if ( outMap.valid() ) { MapNodeOptions outNodeOptions = mapNode->getMapNodeOptions(); osg::ref_ptr<MapNode> outMapNode = new MapNode(outMap.get(), outNodeOptions); if ( !osgDB::writeNodeFile(*outMapNode.get(), outEarthFile) ) { OE_WARN << LC << "Error writing earth file to \"" << outEarthFile << "\"" << std::endl; } else { OE_NOTICE << LC << "Wrote earth file to \"" << outEarthFile << "\"" << std::endl; } } // Mark the progress callback as completed if (_progress.valid()) _progress->onCompleted(); return elevCount + imageCount; }