void
TileModelFactory::createTileModel(const TileKey&           key, 
                                  const MapFrame&          frame,
                                  bool                     accumulate,
                                  osg::ref_ptr<TileModel>& out_model,
                                  ProgressCallback*        progress)
{

    osg::ref_ptr<TileModel> model = new TileModel( frame.getRevision(), frame.getMapInfo() );

    model->_useParentData = _terrainReqs->parentTexturesRequired();

    model->_tileKey = key;
    model->_tileLocator = GeoLocator::createForKey(key, frame.getMapInfo());

    OE_START_TIMER(fetch_imagery);

    // Fetch the image data and make color layers.
    unsigned index = 0;
    unsigned order = 0;
    for( ImageLayerVector::const_iterator i = frame.imageLayers().begin(); i != frame.imageLayers().end(); ++i )
    {
        ImageLayer* layer = i->get();

        if ( layer->getEnabled() && layer->isKeyInRange(key) )
        {
            BuildColorData build;
            build.init( key, layer, order, frame.getMapInfo(), _terrainOptions, _liveTiles.get(), model.get() );

            bool addedToModel = build.execute(progress);
            if ( addedToModel )
            {
                // only bump the order if we added something to the data model.
                order++;
            }
        }
    }

    if (progress)
        progress->stats()["fetch_imagery_time"] += OE_STOP_TIMER(fetch_imagery);

    
    // make an elevation layer.
    OE_START_TIMER(fetch_elevation);
    buildElevation(key, frame, accumulate, _terrainReqs->elevationTexturesRequired(), model.get(), progress);
    if (progress)
        progress->stats()["fetch_elevation_time"] += OE_STOP_TIMER(fetch_elevation);
    
    // make a normal map layer (if necessary)
    if ( _terrainReqs->normalTexturesRequired() )
    {
        OE_START_TIMER(fetch_normalmap);
        buildNormalMap(key, frame, accumulate, model.get(), progress);
        if (progress)
            progress->stats()["fetch_normalmap_time"] += OE_STOP_TIMER(fetch_normalmap);
    }

    // If nothing was added, not even a fallback heightfield, something went
    // horribly wrong. Leave without a tile model. Chances are that a parent tile
    // not not found in the live-tile registry.
    if ( model->_colorData.size() == 0 && !model->_elevationData.getHeightField() )
    {
        return;
    }

    // OK we are making a tile, so if there's no heightfield yet, make an empty one (and mark it
    // as fallback data of course)
    if ( !model->_elevationData.getHeightField() )
    {
        osg::HeightField* hf = HeightFieldUtils::createReferenceHeightField( key.getExtent(), 15, 15 );
        model->_elevationData = TileModel::ElevationData(
            hf,
            GeoLocator::createForKey(key, frame.getMapInfo()),
            true );
    }

    // look up the parent model and cache it.
    osg::ref_ptr<TileNode> parentTile;
    if ( _liveTiles->get(key.createParentKey(), parentTile) )
    {
        model->_parentModel = parentTile->getTileModel();
    }

    out_model = model.release();
}
void
TileModelFactory::createTileModel(const TileKey&           key, 
                                  const MapFrame&          frame,
                                  osg::ref_ptr<TileModel>& out_model) //,
                                  //bool&                    out_hasRealData)
{

    osg::ref_ptr<TileModel> model = new TileModel( frame.getRevision(), frame.getMapInfo() );
    model->_tileKey = key;
    model->_tileLocator = GeoLocator::createForKey(key, frame.getMapInfo());
    
    // Fetch the image data and make color layers.
    unsigned order = 0;
    for( ImageLayerVector::const_iterator i = frame.imageLayers().begin(); i != frame.imageLayers().end(); ++i )
    {
        ImageLayer* layer = i->get();

        if ( layer->getEnabled() )
        {
            BuildColorData build;
            build.init( key, layer, order, frame.getMapInfo(), _terrainOptions, model.get() );
            
            bool addedToModel = build.execute();
            if ( addedToModel )
            {
                // only bump the order if we added something to the data model.
                order++;
            }
        }
    }

    // make an elevation layer.
    BuildElevationData build;
    build.init( key, frame, _terrainOptions, model.get(), _hfCache );
    build.execute();


    // Bail out now if there's no data to be had.
    if ( model->_colorData.size() == 0 && !model->_elevationData.getHeightField() )
    {
        return;
    }

    // OK we are making a tile, so if there's no heightfield yet, make an empty one (and mark it
    // as fallback data of course)
    if ( !model->_elevationData.getHeightField() )
    {
        osg::HeightField* hf = HeightFieldUtils::createReferenceHeightField( key.getExtent(), 15, 15 );
        model->_elevationData = TileModel::ElevationData(
            hf,
            GeoLocator::createForKey(key, frame.getMapInfo()),
            true );
    }

    // look up the parent model and cache it.
    osg::ref_ptr<TileNode> parentTile;
    if ( _liveTiles->get(key.createParentKey(), parentTile) )
        model->_parentModel = parentTile->getTileModel();

    out_model = model.release();
}
Exemple #3
0
bool
HeightFieldCache::getOrCreateHeightField(const MapFrame&                 frame,
                                         const TileKey&                  key,
                                         //bool                            cummulative,
                                         const osg::HeightField*         parent_hf,
                                         osg::ref_ptr<osg::HeightField>& out_hf,
                                         bool&                           out_isFallback,
                                         ElevationSamplePolicy           samplePolicy,
                                         ElevationInterpolation          interp,
                                         ProgressCallback*               progress )
{                
    // default
    out_isFallback = false;

    // check the quick cache.
    HFKey cachekey;
    cachekey._key          = key;
    cachekey._revision     = frame.getRevision();
    cachekey._samplePolicy = samplePolicy;

    if (progress)
        progress->stats()["hfcache_try_count"] += 1;

    bool hit = false;
    LRUCache<HFKey,HFValue>::Record rec;
    if ( _cache.get(cachekey, rec) )
    {
        out_hf = rec.value()._hf.get();
        out_isFallback = rec.value()._isFallback;

        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;
    }

    // Find the parent tile and start with its heightfield.
    if ( parent_hf )
    {
        TileKey parentKey = key.createParentKey();
        
        out_hf = HeightFieldUtils::createSubSample(
            parent_hf,
            parentKey.getExtent(),
            key.getExtent(),
            interp );

        if ( !out_hf.valid() && ((int)key.getLOD())-1 > _firstLOD )
        {
            // This most likely means that a parent tile expired while we were building the child.
            // No harm done in that case as this tile will soo be discarded as well.
            OE_DEBUG << "MP HFC: Unable to find tile " << key.str() << " in the live tile registry"
                << std::endl;
            return false;
        }
    }

    if ( !out_hf.valid() )
    {
        //TODO.
        // This sets the elevation tile size; query size for all tiles.
        out_hf = HeightFieldUtils::createReferenceHeightField(
            key.getExtent(), _tileSize, _tileSize, true );
    }

    bool populated = frame.populateHeightField(
        out_hf,
        key,
        true, // convertToHAE
        samplePolicy,
        progress );

    // Treat Plate Carre specially by scaling the height values. (There is no need
    // to do this with an empty heightfield)
    const MapInfo& mapInfo = frame.getMapInfo();
    if ( mapInfo.isPlateCarre() )
    {
        HeightFieldUtils::scaleHeightFieldToDegrees( out_hf.get() );
    }

    // cache it.
    HFValue cacheval;
    cacheval._hf = out_hf.get();
    cacheval._isFallback = !populated;
    _cache.insert( cachekey, cacheval );

    out_isFallback = !populated;
    return true;
}
bool
HeightFieldCache::getOrCreateHeightField(const MapFrame&                 frame,
        const TileKey&                  key,
        const osg::HeightField*         parent_hf,
        osg::ref_ptr<osg::HeightField>& out_hf,
        bool&                           out_isFallback,
        ElevationSamplePolicy           samplePolicy,
        ElevationInterpolation          interp,
        ProgressCallback*               progress )
{
    // default
    out_isFallback = false;

    // check the quick cache.
    HFKey cachekey;
    cachekey._key          = key;
    cachekey._revision     = frame.getRevision();
    cachekey._samplePolicy = samplePolicy;

    if (progress)
        progress->stats()["hfcache_try_count"] += 1;

    LRUCache<HFKey,HFValue>::Record rec;
    if ( _enabled && _cache.get(cachekey, rec) )
    {
        // Found it in the cache.
        out_hf         = rec.value()._hf.get();
        out_isFallback = rec.value()._isFallback;

        if (progress)
        {
            progress->stats()["hfcache_hit_count"] += 1;
            progress->stats()["hfcache_hit_rate"] = progress->stats()["hfcache_hit_count"]/progress->stats()["hfcache_try_count"];
        }
    }

    else
    {
        // Not in the cache, so we need to create a HF.
        TileKey parentKey = key.createParentKey();

        // Elevation "smoothing" uses the parent HF as the starting point for building
        // a new tile. This will cause lower-resolution data to propagate down the tree
        // and fill in any gaps in higher-resolution data. The result will be an elevation
        // grid that is "smoother" but not neccessarily as accurate.
        if ( _useParentAsReferenceHF && parent_hf && parentKey.valid() )
        {
            out_hf = HeightFieldUtils::createSubSample(
                         parent_hf,
                         parentKey.getExtent(),
                         key.getExtent(),
                         interp );
        }

        // If we are not smoothing, or we have no parent data, start with a basic
        // MSL=0 reference heightfield instead.
        if ( !out_hf.valid() )
        {
            out_hf = HeightFieldUtils::createReferenceHeightField( key.getExtent(), _tileSize, _tileSize, 0u );
        }

        // Next, populate it with data from the Map. The map will overwrite our starting
        // data with real data from the elevation stack.
        bool populated = frame.populateHeightField(
                             out_hf,
                             key,
                             true, // convertToHAE
                             progress );

        // If the map failed to provide any suitable data sources at all, replace the
        // heightfield with data from its parent (if available).
        if ( !populated )
        {
            if ( parentKey.valid() && parent_hf )
            {
                out_hf = HeightFieldUtils::createSubSample(
                             parent_hf,
                             parentKey.getExtent(),
                             key.getExtent(),
                             interp );
            }

            if ( !out_hf.valid() )
            {
                // NOTE: This is probably no longer be possible, but check anyway for completeness.
                return false;
            }
        }

        // ONLY cache the new heightfield if a parent HF existed. Otherwise the new HF
        // may contain invalid data. This can happen if this task runs to completion
        // while the tile's parent expires from the scene graph. In that case the result
        // of this task will be discarded. Therefore we should not cache the result here.
        // This was causing intermittent rare "flat tiles" to appear in the terrain.
        if ( _enabled && parent_hf )
        {
            // cache it.
            HFValue cacheval;
            cacheval._hf = out_hf.get();
            cacheval._isFallback = !populated;
            _cache.insert( cachekey, cacheval );
        }

        out_isFallback = !populated;
    }

    return true;
}