Exemple #1
0
void 
Map::removeElevationLayer( ElevationLayer* layer )
{
    osgEarth::Registry::instance()->clearBlacklist();
    unsigned int index = -1;

    osg::ref_ptr<ElevationLayer> layerToRemove = layer;
    Revision newRevision;

    if ( layerToRemove.get() )
    {
        Threading::ScopedWriteLock lock( _mapDataMutex );
        index = 0;
        for( ElevationLayerVector::iterator i = _elevationLayers.begin(); i != _elevationLayers.end(); i++, index++ )
        {
            if ( i->get() == layerToRemove.get() )
            {
                _elevationLayers.erase( i );
                newRevision = ++_dataModelRevision;
                break;
            }
        }
    }

    // a separate block b/c we don't need the mutex
    if ( newRevision >= 0 ) //layerToRemove.get() )
    {
        for( MapCallbackList::iterator i = _mapCallbacks.begin(); i != _mapCallbacks.end(); i++ )
        {
            i->get()->onMapModelChanged( MapModelChange(
                MapModelChange::REMOVE_ELEVATION_LAYER, newRevision, layerToRemove.get(), index) );
        }
    }
}
Exemple #2
0
void
Map::setCache( Cache* cache )
{
    if (_cache.get() != cache)
    {
        _cache = cache;

        if ( _cache.valid() )
        {
            _cache->store( _dbOptions.get() );
        }

        // Propagate the cache to any of our layers
        for (ImageLayerVector::iterator i = _imageLayers.begin(); i != _imageLayers.end(); ++i)
        {
            i->get()->setDBOptions( _dbOptions.get() );
            //i->get()->setCache( _cache.get() );
        }

        for (ElevationLayerVector::iterator i = _elevationLayers.begin(); i != _elevationLayers.end(); ++i)
        {
            i->get()->setDBOptions( _dbOptions.get() );
            //i->get()->setCache( _cache.get() );
        }
    }
}
Exemple #3
0
 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();
 }
Exemple #4
0
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()) );
    }
}
Exemple #5
0
void
Map::moveElevationLayer( ElevationLayer* layer, unsigned int newIndex )
{
    unsigned int oldIndex = 0;
    unsigned int actualIndex = 0;
    Revision newRevision;

    if ( layer )
    {
        Threading::ScopedWriteLock lock( _mapDataMutex );

        // preserve the layer with a ref:
        osg::ref_ptr<ElevationLayer> layerToMove = layer;

        // find it:
        ElevationLayerVector::iterator i_oldIndex = _elevationLayers.end();
        for( ElevationLayerVector::iterator i = _elevationLayers.begin(); i != _elevationLayers.end(); i++, actualIndex++ )
        {
            if ( i->get() == layer )
            {
                i_oldIndex = i;
                oldIndex = actualIndex;
                break;
            }
        }

        if ( i_oldIndex == _elevationLayers.end() )
            return; // layer not found in list

        // erase the old one and insert the new one.
        _elevationLayers.erase( i_oldIndex );
        _elevationLayers.insert( _elevationLayers.begin() + newIndex, layerToMove.get() );

        newRevision = ++_dataModelRevision;
    }

    // a separate block b/c we don't need the mutex
    if ( layer )
    {
        for( MapCallbackList::iterator i = _mapCallbacks.begin(); i != _mapCallbacks.end(); i++ )
        {
            i->get()->onMapModelChanged( MapModelChange(
                MapModelChange::MOVE_ELEVATION_LAYER, newRevision, layer, oldIndex, newIndex) );
        }
    }
}
Exemple #6
0
void
Map::setCache( Cache* cache )
{
    if (_cache.get() != cache)
    {
        _cache = cache;
        _cache->setReferenceURI( _mapOptions.referenceURI().value() );

        //Propagate the cache to any of our layers
        for (ImageLayerVector::iterator i = _imageLayers.begin(); i != _imageLayers.end(); ++i)
        {
            i->get()->setCache( _cache.get() );
        }

        for (ElevationLayerVector::iterator i = _elevationLayers.begin(); i != _elevationLayers.end(); ++i)
        {
            i->get()->setCache( _cache.get() );
        }
    }
}
Exemple #7
0
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() );
    }
}
/** 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;
}
Exemple #9
0
void
Map::calculateProfile()
{
    if ( !_profile.valid() )
    {
        osg::ref_ptr<const Profile> userProfile;
        if ( _mapOptions.profile().isSet() )
        {
            userProfile = Profile::create( _mapOptions.profile().value() );
        }

        if ( _mapOptions.coordSysType() == MapOptions::CSTYPE_GEOCENTRIC )
        {
            if ( userProfile.valid() )
            {
                if ( userProfile->isOK() && userProfile->getSRS()->isGeographic() )
                {
                    _profile = userProfile.get();
                }
                else
                {
                    OE_WARN << LC 
                        << "Map is geocentric, but the configured profile SRS ("
                        << userProfile->getSRS()->getName() << ") is not geographic; "
                        << "it will be ignored."
                        << std::endl;
                }
            }

            if ( !_profile.valid() )
            {
                // by default, set a geocentric map to use global-geodetic WGS84.
                _profile = osgEarth::Registry::instance()->getGlobalGeodeticProfile();
            }
        }

        else if ( _mapOptions.coordSysType() == MapOptions::CSTYPE_GEOCENTRIC_CUBE )
        {
            //If the map type is a Geocentric Cube, set the profile to the cube profile.
            _profile = osgEarth::Registry::instance()->getCubeProfile();
        }

        else // CSTYPE_PROJECTED
        {
            if ( userProfile.valid() )
            {
                _profile = userProfile.get();
            }
        }

        // At this point, if we don't have a profile we need to search tile sources until we find one.
        if ( !_profile.valid() )
        {
            Threading::ScopedReadLock lock( _mapDataMutex );

            for( ImageLayerVector::iterator i = _imageLayers.begin(); i != _imageLayers.end() && !_profile.valid(); i++ )
            {
                ImageLayer* layer = i->get();
                if ( layer->getTileSource() )
                {
                    _profile = layer->getTileSource()->getProfile();
                }
            }

            for( ElevationLayerVector::iterator i = _elevationLayers.begin(); i != _elevationLayers.end() && !_profile.valid(); i++ )
            {
                ElevationLayer* layer = i->get();
                if ( layer->getTileSource() )
                {
                    _profile = layer->getTileSource()->getProfile();
                }
            }
        }

        // convert the profile to Plate Carre if necessary.
        if (_profile.valid() &&
            _profile->getSRS()->isGeographic() && 
            getMapOptions().coordSysType() == MapOptions::CSTYPE_PROJECTED )
        {
            OE_INFO << LC << "Projected display with geographic SRS; activating Plate Carre mode" << std::endl;
            _profile = _profile->overrideSRS( _profile->getSRS()->createPlateCarreGeographicSRS() );
        }

        // finally, fire an event if the profile has been set.
        if ( _profile.valid() )
        {
            OE_INFO << LC << "Map profile is: " << _profile->toString() << std::endl;

            for( MapCallbackList::iterator i = _mapCallbacks.begin(); i != _mapCallbacks.end(); i++ )
            {
                i->get()->onMapInfoEstablished( MapInfo(this) );
            }
        }

        else
        {
            OE_WARN << LC << "Warning, not yet able to establish a map profile!" << std::endl;
        }
    }

    if ( _profile.valid() )
    {
        // tell all the loaded layers what the profile is, as a hint
        {
            Threading::ScopedWriteLock lock( _mapDataMutex );

            for( ImageLayerVector::iterator i = _imageLayers.begin(); i != _imageLayers.end(); i++ )
            {
                ImageLayer* layer = i->get();
                if ( layer->getEnabled() == true )
                {
                    layer->setTargetProfileHint( _profile.get() );
                }
            }

            for( ElevationLayerVector::iterator i = _elevationLayers.begin(); i != _elevationLayers.end(); i++ )
            {
                ElevationLayer* layer = i->get();
                if ( layer->getEnabled() )
                {
                    layer->setTargetProfileHint( _profile.get() );
                }
            }
        }

        // create a "proxy" profile to use when querying elevation layers with a vertical datum
        if ( _profile->getSRS()->getVerticalDatum() != 0L )
        {
            ProfileOptions po = _profile->toProfileOptions();
            po.vsrsString().unset();
            _profileNoVDatum = Profile::create(po);
        }
        else
        {
            _profileNoVDatum = _profile;
        }
    }
}
/** 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;
}
Exemple #11
0
void
Map::calculateProfile()
{
    if ( !_profile.valid() )
    {
        osg::ref_ptr<const Profile> userProfile;
        if ( _mapOptions.profile().isSet() )
        {
            userProfile = Profile::create( _mapOptions.profile().value() );
        }

        if ( _mapOptions.coordSysType() == MapOptions::CSTYPE_GEOCENTRIC )
        {
            if ( userProfile.valid() )
            {
                if ( userProfile->isOK() && userProfile->getSRS()->isGeographic() )
                {
                    _profile = userProfile.get();
                }
                else
                {
                    OE_WARN << LC 
                        << "Map is geocentric, but the configured profile does not "
                        << "have a geographic SRS. Falling back on default.."
                        << std::endl;
                }
            }

            if ( !_profile.valid() )
            {
                // by default, set a geocentric map to use global-geodetic WGS84.
                _profile = osgEarth::Registry::instance()->getGlobalGeodeticProfile();
            }
        }

        else if ( _mapOptions.coordSysType() == MapOptions::CSTYPE_GEOCENTRIC_CUBE )
        {
            //If the map type is a Geocentric Cube, set the profile to the cube profile.
            _profile = osgEarth::Registry::instance()->getCubeProfile();
        }

        else // CSTYPE_PROJECTED
        {
            if ( userProfile.valid() )
            {
                _profile = userProfile.get();
            }
        }

        // At this point, if we don't have a profile we need to search tile sources until we find one.
        if ( !_profile.valid() )
        {
            Threading::ScopedReadLock lock( _mapDataMutex );

            for( ImageLayerVector::iterator i = _imageLayers.begin(); i != _imageLayers.end() && !_profile.valid(); i++ )
            {
                ImageLayer* layer = i->get();
                if ( layer->getTileSource() )
                {
                    _profile = layer->getTileSource()->getProfile();
                }
            }

            for( ElevationLayerVector::iterator i = _elevationLayers.begin(); i != _elevationLayers.end() && !_profile.valid(); i++ )
            {
                ElevationLayer* layer = i->get();
                if ( layer->getTileSource() )
                {
                    _profile = layer->getTileSource()->getProfile();
                }
            }
        }

        // finally, fire an event if the profile has been set.
        if ( _profile.valid() )
        {
            OE_INFO << LC << "Map profile is: " << _profile->toString() << std::endl;

            for( MapCallbackList::iterator i = _mapCallbacks.begin(); i != _mapCallbacks.end(); i++ )
            {
                i->get()->onMapInfoEstablished( MapInfo(this) );
            }
        }

        else
        {
            OE_WARN << LC << "Warning, not yet able to establish a map profile!" << std::endl;
        }
    }

    if ( _profile.valid() )
    {
        // tell all the loaded layers what the profile is, as a hint
        {
            Threading::ScopedWriteLock lock( _mapDataMutex );

            for( ImageLayerVector::iterator i = _imageLayers.begin(); i != _imageLayers.end(); i++ )
            {
                ImageLayer* layer = i->get();
                layer->setTargetProfileHint( _profile.get() );
            }

            for( ElevationLayerVector::iterator i = _elevationLayers.begin(); i != _elevationLayers.end(); i++ )
            {
                ElevationLayer* layer = i->get();
                layer->setTargetProfileHint( _profile.get() );
            }
        }
    }
}
/** 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;
}