MapNode*
EarthFileSerializer2::deserialize( const Config& conf, const std::string& referenceURI ) const
{
    MapOptions mapOptions( conf.child( "options" ) );

    // legacy: check for name/type in top-level attrs:
    if ( conf.hasValue( "name" ) || conf.hasValue( "type" ) )
    {
        Config legacy;
        if ( conf.hasValue("name") ) legacy.add( "name", conf.value("name") );
        if ( conf.hasValue("type") ) legacy.add( "type", conf.value("type") );
        mapOptions.mergeConfig( legacy );
    }

    Map* map = new Map( mapOptions );

    // Yes, MapOptions and MapNodeOptions share the same Config node. Weird but true.
    MapNodeOptions mapNodeOptions( conf.child( "options" ) );

    // Read the layers in LAST (otherwise they will not benefit from the cache/profile configuration)

    // Image layers:
    ConfigSet images = conf.children( "image" );
    for( ConfigSet::const_iterator i = images.begin(); i != images.end(); i++ )
    {
        Config layerDriverConf = *i;
        layerDriverConf.add( "default_tile_size", "256" );

        ImageLayerOptions layerOpt( layerDriverConf );
        layerOpt.name() = layerDriverConf.value("name");
        //layerOpt.driver() = TileSourceOptions( layerDriverConf );

        map->addImageLayer( new ImageLayer(layerOpt) );
    }

    // Elevation layers:
    for( int k=0; k<2; ++k )
    {
        std::string tagName = k == 0 ? "elevation" : "heightfield"; // support both :)

        ConfigSet heightfields = conf.children( tagName );
        for( ConfigSet::const_iterator i = heightfields.begin(); i != heightfields.end(); i++ )
        {
            Config layerDriverConf = *i;
            layerDriverConf.add( "default_tile_size", "16" );

            ElevationLayerOptions layerOpt( layerDriverConf );
            layerOpt.name() = layerDriverConf.value( "name" );
            //layerOpt.driver() = TileSourceOptions( layerDriverConf );

            map->addElevationLayer( new ElevationLayer(layerOpt) );
        }
    }

    // Model layers:
    ConfigSet models = conf.children( "model" );
    for( ConfigSet::const_iterator i = models.begin(); i != models.end(); i++ )
    {
        const Config& layerDriverConf = *i;

        ModelLayerOptions layerOpt( layerDriverConf );
        layerOpt.name() = layerDriverConf.value( "name" );
        layerOpt.driver() = ModelSourceOptions( layerDriverConf );

        map->addModelLayer( new ModelLayer(layerOpt) );
        //map->addModelLayer( new ModelLayer( layerDriverConf.value("name"), ModelSourceOptions(*i) ) );
    }

    // Overlay layers (just an alias for Model Layer with overlay=true)
    ConfigSet overlays = conf.children( "overlay" );
    for( ConfigSet::const_iterator i = overlays.begin(); i != overlays.end(); i++ )
    {
        Config layerDriverConf = *i;
        if ( !layerDriverConf.hasValue("driver") )
            layerDriverConf.set("driver", "feature_geom");

        ModelLayerOptions layerOpt( layerDriverConf );
        layerOpt.name() = layerDriverConf.value( "name" );
        layerOpt.driver() = ModelSourceOptions( layerDriverConf );
        layerOpt.overlay() = true; // forced on when "overlay" specified

        map->addModelLayer( new ModelLayer(layerOpt) );
    }

    // Mask layer:
    ConfigSet masks = conf.children( "mask" );
    for( ConfigSet::const_iterator i = masks.begin(); i != masks.end(); i++ )
    {
        Config maskLayerConf = *i;

        MaskLayerOptions options(maskLayerConf);
        options.name() = maskLayerConf.value( "name" );
        options.driver() = MaskSourceOptions(options);

        map->addTerrainMaskLayer( new MaskLayer(options) );
    }

    
    //Add any addition paths specified in the options/osg_file_paths element to the file path.  Useful for pointing osgEarth at resource folders.
    Config osg_file_paths = conf.child( "options" ).child("osg_file_paths");
    ConfigSet urls = osg_file_paths.children("url");
    for (ConfigSet::const_iterator i = urls.begin(); i != urls.end(); i++) 
    {
        std::string path = osgEarth::getFullPath( referenceURI, (*i).value());
        OE_DEBUG << "Adding OSG file path " << path << std::endl;
        osgDB::Registry::instance()->getDataFilePathList().push_back( path );
    }

    MapNode* mapNode = new MapNode( map, mapNodeOptions );

    // External configs:
    Config ext = conf.child( "external" );
    if ( !ext.empty() )
    {
        mapNode->externalConfig() = ext;
    }

    return mapNode;
}
MapNode*
EarthFileSerializer2::deserialize( const Config& conf, const std::string& referenceURI ) const
{
    MapOptions mapOptions( conf.child( "options" ) );

    //Set the reference URI of the cache config.
    if (mapOptions.cache().isSet())
    {
        mapOptions.cache()->setReferenceURI(referenceURI);
    }

    // the reference URI allows osgEarth to resolve relative paths within the configuration
    mapOptions.referenceURI() = referenceURI;

    // manually extract the "type" from the main tag:
    const std::string& csVal = conf.value("type");
    mapOptions.coordSysType() = 
        csVal == "cube" ? MapOptions::CSTYPE_GEOCENTRIC_CUBE :
        csVal == "projected" || csVal == "flat" ? MapOptions::CSTYPE_PROJECTED :
        MapOptions::CSTYPE_GEOCENTRIC;

    // legacy: check for name/type in top-level attrs:
    if ( conf.hasValue( "name" ) || conf.hasValue( "type" ) )
    {
        Config legacy;
        if ( conf.hasValue("name") ) legacy.add( "name", conf.value("name") );
        if ( conf.hasValue("type") ) legacy.add( "type", conf.value("type") );
        mapOptions.mergeConfig( legacy );
    }

    Map* map = new Map( mapOptions );

    // Yes, MapOptions and MapNodeOptions share the same Config node. Weird but true.
    MapNodeOptions mapNodeOptions( conf.child( "options" ) );

    // Read the layers in LAST (otherwise they will not benefit from the cache/profile configuration)

    // Image layers:
    ConfigSet images = conf.children( "image" );
    for( ConfigSet::const_iterator i = images.begin(); i != images.end(); i++ )
    {
        Config layerDriverConf = *i;
        layerDriverConf.add( "default_tile_size", "256" );

        ImageLayerOptions layerOpt( layerDriverConf );
        layerOpt.name() = layerDriverConf.value("name");
        //layerOpt.driver() = TileSourceOptions( layerDriverConf );

        map->addImageLayer( new ImageLayer(layerOpt) );
    }

    // Elevation layers:
    for( int k=0; k<2; ++k )
    {
        std::string tagName = k == 0 ? "elevation" : "heightfield"; // support both :)

        ConfigSet heightfields = conf.children( tagName );
        for( ConfigSet::const_iterator i = heightfields.begin(); i != heightfields.end(); i++ )
        {
            Config layerDriverConf = *i;
            layerDriverConf.add( "default_tile_size", "16" );

            ElevationLayerOptions layerOpt( layerDriverConf );
            layerOpt.name() = layerDriverConf.value( "name" );
            //layerOpt.driver() = TileSourceOptions( layerDriverConf );

            map->addElevationLayer( new ElevationLayer(layerOpt) );
        }
    }

    // Model layers:
    ConfigSet models = conf.children( "model" );
    for( ConfigSet::const_iterator i = models.begin(); i != models.end(); i++ )
    {
        const Config& layerDriverConf = *i;

        ModelLayerOptions layerOpt( layerDriverConf );
        layerOpt.name() = layerDriverConf.value( "name" );
        layerOpt.driver() = ModelSourceOptions( layerDriverConf );

        map->addModelLayer( new ModelLayer(layerOpt) );
        //map->addModelLayer( new ModelLayer( layerDriverConf.value("name"), ModelSourceOptions(*i) ) );
    }

    // Overlay layers (just an alias for Model Layer with overlay=true)
    ConfigSet overlays = conf.children( "overlay" );
    for( ConfigSet::const_iterator i = overlays.begin(); i != overlays.end(); i++ )
    {
        Config layerDriverConf = *i;
        if ( !layerDriverConf.hasValue("driver") )
            layerDriverConf.attr("driver") = "feature_geom";
        //const Config& layerDriverConf = *i;

        ModelLayerOptions layerOpt( layerDriverConf );
        layerOpt.name() = layerDriverConf.value( "name" );
        layerOpt.driver() = ModelSourceOptions( layerDriverConf );
        layerOpt.overlay() = true; // forced on when "overlay" specified

        map->addModelLayer( new ModelLayer(layerOpt) );
    }

    // Mask layer:
    Config maskLayerConf = conf.child( "mask" );
    if ( !maskLayerConf.empty() )
    {
        MaskLayerOptions options(maskLayerConf);
        options.name() = maskLayerConf.value( "name" );
        options.driver() = MaskSourceOptions(options);
        map->setTerrainMaskLayer( new MaskLayer(options) );
    }

    MapNode* mapNode = new MapNode( map, mapNodeOptions );

    // External configs:
    Config ext = conf.child( "external" );
    if ( !ext.empty() )
    {
        mapNode->externalConfig() = ext;
    }

    return mapNode;
}
osg::Node*
EarthFileSerializer2::deserialize( const Config& conf, const std::string& referrer ) const
{
    // First, pre-load any extension DLLs.
    preloadExtensionLibs(conf);
    preloadExtensionLibs(conf.child("extensions"));
    preloadExtensionLibs(conf.child("external"));

    MapOptions mapOptions( conf.child( "options" ) );

    // legacy: check for name/type in top-level attrs:
    if ( conf.hasValue( "name" ) || conf.hasValue( "type" ) )
    {
        Config legacy;
        if ( conf.hasValue("name") ) legacy.add( "name", conf.value("name") );
        if ( conf.hasValue("type") ) legacy.add( "type", conf.value("type") );
        mapOptions.mergeConfig( legacy );
    }

    Map* map = new Map( mapOptions );

    // Yes, MapOptions and MapNodeOptions share the same Config node. Weird but true.
    MapNodeOptions mapNodeOptions( conf.child( "options" ) );

    // Create a map node.
    MapNode* mapNode = new MapNode( map, mapNodeOptions );

    // Read the layers in LAST (otherwise they will not benefit from the cache/profile configuration)
    for(ConfigSet::const_iterator i = conf.children().begin(); i != conf.children().end(); ++i)
    {
        if (i->key() == "options" || i->key() == "name" || i->key() == "type" || i->key() == "version")
        {
            // nop - handled earlier
        }

        else if ( i->key() == "image" )
        {
            addImageLayer( *i, map );
        }

        else if ( i->key() == "elevation" || i->key() == "heightfield" )
        {
            addElevationLayer( *i, map );
        }

        else if ( i->key() == "model" )
        {
            addModelLayer( *i, map );
        }

        else if ( i->key() == "mask" )
        {
            addMaskLayer( *i, map );
        }

        else if ( i->key() == "external" || i->key() == "extensions" )
        {
            mapNode->externalConfig() = *i;
            for(ConfigSet::const_iterator e = i->children().begin(); e != i->children().end(); ++e)
            {
                addExtension( *e, mapNode );
            }
        }

        else // plugins/extensions.
        {
            addExtension( *i, mapNode );
        }
    }

    // return the topmost parent of the mapnode. It's possible that
    // an extension added parents!
    osg::Node* top = mapNode;
    while( top->getNumParents() > 0 )
        top = top->getParent(0);

    return top;
}