コード例 #1
0
ファイル: OSGTileFactory.cpp プロジェクト: hulumogu/osgearth
bool
OSGTileFactory::createValidGeoImage(ImageLayer* layer,
                                    const TileKey& key,
                                    GeoImage& out_image,
                                    TileKey&  out_actualTileKey,
                                    ProgressCallback* progress)
{
    //TODO:  Redo this to just grab images from the parent TerrainTiles
    //Try to create the image with the given key
    out_actualTileKey = key;

    while (out_actualTileKey.valid())
    {
        if ( layer->isKeyValid(out_actualTileKey) )
        {
            out_image = layer->createImage( out_actualTileKey, progress );
            if ( out_image.valid() )
            {
                return true;
            }
        }
        out_actualTileKey = out_actualTileKey.createParentKey();
    }
    return false;
}
コード例 #2
0
void
TileModelFactory::buildElevation(const TileKey&    key,
                                 const MapFrame&   frame,
                                 bool              accumulate,
                                 TileModel*        model,
                                 ProgressCallback* progress)
{     
    const MapInfo& mapInfo = frame.getMapInfo();

    const osgEarth::ElevationInterpolation& interp =
        frame.getMapOptions().elevationInterpolation().get();

    // Request a heightfield from the map, falling back on lower resolution tiles
    // if necessary (fallback=true)
    osg::ref_ptr<osg::HeightField> hf;

    bool isFallback = false;

    if (_hfCache->getOrCreateHeightField(frame, key, accumulate, hf, isFallback, SAMPLE_FIRST_VALID, interp, progress))
    {
        model->_elevationData = TileModel::ElevationData(
            hf,
            GeoLocator::createForKey( key, mapInfo ),
            isFallback );

        // Edge normalization: requires adjacency information
        if ( _terrainOptions.normalizeEdges() == true )
        {
            for( int x=-1; x<=1; x++ )
            {
                for( int y=-1; y<=1; y++ )
                {
                    if ( x != 0 || y != 0 )
                    {
                        TileKey nk = key.createNeighborKey(x, y);
                        if ( nk.valid() )
                        {
                            osg::ref_ptr<osg::HeightField> hf;
                            if (_hfCache->getOrCreateHeightField(frame, nk, accumulate, hf, isFallback, SAMPLE_FIRST_VALID, interp, progress) )
                            {
                                model->_elevationData.setNeighbor( x, y, hf.get() );
                            }
                        }
                    }
                }
            }

            // parent too.
            if ( key.getLOD() > 0 )
            {
                osg::ref_ptr<osg::HeightField> hf;
                if ( _hfCache->getOrCreateHeightField(frame, key.createParentKey(), accumulate, hf, isFallback, SAMPLE_FIRST_VALID, interp, progress) )
                {
                    model->_elevationData.setParent( hf.get() );
                }
            }
        }
    }
}
コード例 #3
0
osg::Node*
MPTerrainEngineNode::createTile( const TileKey& key )
{
    osg::ref_ptr<TileModel> model = new TileModel( _update_mapf->getRevision(), _update_mapf->getMapInfo() );
    model->_tileKey = key;
    model->_tileLocator = GeoLocator::createForKey(key, _update_mapf->getMapInfo());

    // Build the heightfield

    const MapInfo& mapInfo = _update_mapf->getMapInfo();

    const osgEarth::ElevationInterpolation& interp = _update_mapf->getMapOptions().elevationInterpolation().get();

    // Request a heightfield from the map, falling back on lower resolution tiles
    osg::ref_ptr<osg::HeightField> hf;    

    TileKey sampleKey = key;
    bool populated = false;
    if (_update_mapf->elevationLayers().size() > 0)
    {
        while (!populated)
        {
            populated = _update_mapf->populateHeightField(hf, sampleKey, true, SAMPLE_FIRST_VALID);
            if (!populated)
            {
                // Fallback on the parent
                sampleKey = sampleKey.createParentKey();
                if (!sampleKey.valid())
                {
                    return 0;
                }
            }
        }       
    }

    if (!populated)
    {
        // We have no heightfield so just create a reference heightfield.
        hf = HeightFieldUtils::createReferenceHeightField( key.getExtent(), 15, 15 );
        sampleKey = key;
    }

    model->_elevationData = TileModel::ElevationData(
            hf,
            GeoLocator::createForKey( sampleKey, mapInfo ),
            false );        

    bool optimizeTriangleOrientation = getMap()->getMapOptions().elevationInterpolation() != INTERP_TRIANGULATE;

    osg::ref_ptr<TileModelCompiler> compiler = new TileModelCompiler(
            _update_mapf->terrainMaskLayers(),
            _update_mapf->modelLayers(),
            _primaryUnit,
            optimizeTriangleOrientation,
            _terrainOptions );

    return compiler->compile(model.get(), *_update_mapf, 0L);
}
コード例 #4
0
osg::Node*
RexTerrainEngineNode::createTile( const TileKey& key )
{
     // Compute the sample size to use for the key's level of detail that will line up exactly with the tile size of the highest level of subdivision of the rex engine.
    unsigned int sampleSize = computeSampleSize( key.getLevelOfDetail() );    
    OE_INFO << LC << "Computed a sample size of " << sampleSize << " for lod " << key.getLevelOfDetail() << std::endl;

    TileKey sampleKey = key;

    // ALWAYS use 257x257 b/c that is what rex always uses.
    osg::ref_ptr< osg::HeightField > out_hf = HeightFieldUtils::createReferenceHeightField(
            key.getExtent(), 257, 257, 0u, true );

    sampleKey = key;

    bool populated = false;
    while (!populated)
    {
        populated = _update_mapf->populateHeightField(
            out_hf,
            sampleKey,
            true, // convertToHAE
            0 );

        if (!populated)
        {
            // Fallback on the parent
            sampleKey = sampleKey.createParentKey();
            if (!sampleKey.valid())
            {
                return 0;
            }
        }
    }

    // cannot happen (says coverity; see loop above), so commenting this out -gw
#if 0
    if (!populated)
    {
        // We have no heightfield so just create a reference heightfield.
        out_hf = HeightFieldUtils::createReferenceHeightField( key.getExtent(), 257, 257, 0u);
        sampleKey = key;
    }
#endif

    GeoHeightField geoHF( out_hf.get(), sampleKey.getExtent() );    
    if (sampleKey != key)
    {   
        geoHF = geoHF.createSubSample( key.getExtent(), sampleSize, sampleSize, osgEarth::INTERP_BILINEAR);         
    }

    // We should now have a heightfield that matches up exactly with the requested key at the appropriate resolution.
    // Turn it into triangles.
    return renderHeightField( geoHF );      
}
コード例 #5
0
ファイル: ElevationPool.cpp プロジェクト: filnet/osgearth
bool
ElevationPool::fetchTileFromMap(const TileKey& key, MapFrame& frame, 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 FULL MAP (" << keyToUse.str() << ")\n";
            ok = frame.populateHeightField(hf, keyToUse, false /*heightsAsHAE*/, 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();
}
コード例 #6
0
bool
ElevationLayerVector::populateHeightFieldAndNormalMap(osg::HeightField*      hf,
                                                      NormalMap*             normalMap,
                                                      const TileKey&         key,
                                                      const Profile*         haeProfile,
                                                      ElevationInterpolation interpolation,
                                                      ProgressCallback*      progress ) const
{
    // heightfield must already exist.
    if ( !hf )
        return false;

    METRIC_SCOPED("ElevationLayer.populateHeightField");

    // 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.
    LayerDataVector contenders;
    LayerDataVector offsets;

#ifdef ANALYZE
    struct LayerAnalysis {
        LayerAnalysis() : samples(0), used(false), failed(false), fallback(false), actualKeyValid(true) { }
        int samples; bool used; bool failed; bool fallback; bool actualKeyValid; std::string message;
    };
    std::map<ElevationLayer*, LayerAnalysis> layerAnalysis;
#endif

    // Track the number of layers that would return fallback data.
    unsigned numFallbackLayers = 0;

    // Check them in reverse order since the highest priority is last.
    for (int i = size()-1; i>=0; --i)
    //for(ElevationLayerVector::const_reverse_iterator i = this->rbegin(); i != this->rend(); ++i)
    {
        ElevationLayer* layer = (*this)[i].get(); //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() );

            bool useLayer = true;
            TileKey bestKey( mappedKey );

            // Check whether the non-mapped key is valid according to the user's min/max level settings:
            if ( !layer->isKeyInLegalRange(key) )
            {
                useLayer = false;
            }
                
            // Find the "best available" mapped key from the tile source:
            else 
            {
                bestKey = layer->getBestAvailableTileKey(mappedKey);
                if (bestKey.valid())
                {
                    // If the bestKey is not the mappedKey, this layer is providing
                    // fallback data (data at a lower resolution than requested)
                    if ( mappedKey != bestKey )
                    {
                        numFallbackLayers++;
                    }
                }
                else
                {
                    useLayer = false;
                }
            }

            if ( useLayer )
            {
                if ( layer->isOffset() )
                {
                    offsets.push_back(LayerData());
                    LayerData& ld = offsets.back();
                    ld.layer = layer;
                    ld.key = bestKey;
                    ld.index = i;
                }
                else
                {
                    contenders.push_back(LayerData());
                    LayerData& ld = contenders.back();
                    ld.layer = layer;
                    ld.key = bestKey;
                    ld.index = i;
                }

#ifdef ANALYZE
                layerAnalysis[layer].used = true;
#endif
            }
        }
    }

    // nothing? bail out.
    if ( contenders.empty() && offsets.empty() )
    {
        return false;
    }

    // if everything is fallback data, bail out.
    if ( contenders.size() + offsets.size() == numFallbackLayers )
    {
        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>    heightFallback(contenders.size(), false);
    std::vector<bool>    heightFailed(contenders.size(), false);
    std::vector<bool>    offsetFailed(offsets.size(), false);

    // The maximum number of heightfields to keep in this local cache
    const unsigned maxHeightFields = 50;
    unsigned numHeightFieldsInCache = 0;

    const SpatialReference* keySRS = keyToUse.getProfile()->getSRS();

    bool realData = false;

    unsigned int total = numColumns * numRows;

    // query resolution interval (x, y) of each sample.
    osg::ref_ptr<osg::ShortArray> deltaLOD = new osg::ShortArray(total);
    
    int nodataCount = 0;

    TileKey scratchKey; // Storage if a new key needs to be constructed

    bool requiresResample = true;

    // If we only have a single contender layer, and the tile is the same size as the requested 
    // heightfield then we just use it directly and avoid having to resample it
    if (contenders.size() == 1 && offsets.empty())
    {
        ElevationLayer* layer = contenders[0].layer.get();
        TileKey& contenderKey = contenders[0].key;

        GeoHeightField layerHF = layer->createHeightField(contenderKey, 0);
        if (layerHF.valid())
        {
            if (layerHF.getHeightField()->getNumColumns() == hf->getNumColumns() &&
                layerHF.getHeightField()->getNumRows() == hf->getNumRows())
            {
                requiresResample = false;
                memcpy(hf->getFloatArray()->asVector().data(),
                    layerHF.getHeightField()->getFloatArray()->asVector().data(),
                    sizeof(float) * hf->getFloatArray()->size()
                );
                deltaLOD->resize(hf->getFloatArray()->size(), 0);
                realData = true;
            }
        }
    }

    // If we need to mosaic multiple layers or resample it to a new output tilesize go through a resampling loop.
    if (requiresResample)
    {
        for (unsigned c = 0; c < numColumns; ++c)
        {
            double x = xmin + (dx * (double)c);

            // periodically check for cancelation
            if (progress && progress->isCanceled())
            {
                return false;
            }

            for (unsigned r = 0; r < numRows; ++r)
            {
                double y = ymin + (dy * (double)r);

                // Collect elevations from each layer as necessary.
                int resolvedIndex = -1;

                osg::Vec3 normal_sum(0, 0, 0);

                for (int i = 0; i < contenders.size() && resolvedIndex < 0; ++i)
                {
                    ElevationLayer* layer = contenders[i].layer.get();
                    TileKey& contenderKey = contenders[i].key;
                    int index = contenders[i].index;

                    if (heightFailed[i])
                        continue;

                    TileKey* actualKey = &contenderKey;

                    GeoHeightField& layerHF = heightFields[i];

                    if (!layerHF.valid())
                    {
                        // We couldn't get the heightfield from the cache, so try to create it.
                        // We also fallback on parent layers to make sure that we have data at the location even if it's fallback.
                        while (!layerHF.valid() && actualKey->valid() && layer->isKeyInLegalRange(*actualKey))
                        {
                            layerHF = layer->createHeightField(*actualKey, progress);
                            if (!layerHF.valid())
                            {
                                if (actualKey != &scratchKey)
                                {
                                    scratchKey = *actualKey;
                                    actualKey = &scratchKey;
                                }
                                *actualKey = actualKey->createParentKey();
                            }
                        }

                        // Mark this layer as fallback if necessary.
                        if (layerHF.valid())
                        {
                            heightFallback[i] = (*actualKey != contenderKey); // actualKey != contenders[i].second;
                            numHeightFieldsInCache++;
                        }
                        else
                        {
                            heightFailed[i] = true;
#ifdef ANALYZE
                            layerAnalysis[layer].failed = true;
                            layerAnalysis[layer].actualKeyValid = actualKey->valid();
                            if (progress) layerAnalysis[layer].message = progress->message();
#endif
                            continue;
                        }
                    }

                    if (layerHF.valid())
                    {
                        bool isFallback = heightFallback[i];
#ifdef ANALYZE
                        layerAnalysis[layer].fallback = isFallback;
#endif

                        // We only have real data if this is not a fallback heightfield.
                        if (!isFallback)
                        {
                            realData = true;
                        }

                        float elevation;
                        if (layerHF.getElevation(keySRS, x, y, interpolation, keySRS, elevation))
                        {
                            if (elevation != NO_DATA_VALUE)
                            {
                                // remember the index so we can only apply offset layers that
                                // sit on TOP of this layer.
                                resolvedIndex = index;

                                hf->setHeight(c, r, elevation);

#ifdef ANALYZE
                                layerAnalysis[layer].samples++;
#endif

                                if (deltaLOD)
                                {
                                    (*deltaLOD)[r*numColumns + c] = key.getLOD() - actualKey->getLOD();
                                }
                            }
                            else
                            {
                                ++nodataCount;
                            }
                        }
                    }


                    // Clear the heightfield cache if we have too many heightfields in the cache.
                    if (numHeightFieldsInCache >= maxHeightFields)
                    {
                        //OE_NOTICE << "Clearing cache" << std::endl;
                        for (unsigned int k = 0; k < heightFields.size(); k++)
                        {
                            heightFields[k] = GeoHeightField::INVALID;
                            heightFallback[k] = false;
                        }
                        numHeightFieldsInCache = 0;
                    }
                }

                for (int i = offsets.size() - 1; i >= 0; --i)
                {
                    // Only apply an offset layer if it sits on top of the resolved layer
                    // (or if there was no resolved layer).
                    if (resolvedIndex >= 0 && offsets[i].index < resolvedIndex)
                        continue;

                    TileKey &contenderKey = offsets[i].key;

                    if (offsetFailed[i] == true)
                        continue;

                    GeoHeightField& layerHF = offsetFields[i];
                    if (!layerHF.valid())
                    {
                        ElevationLayer* offset = offsets[i].layer.get();

                        layerHF = offset->createHeightField(contenderKey, 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;

                        // Update the resolution tracker to account for the offset. Sadly this
                        // will wipe out the resolution of the actual data, and might result in 
                        // normal faceting. See the comments on "createNormalMap" for more info
                        if (deltaLOD)
                        {
                            (*deltaLOD)[r*numColumns + c] = key.getLOD() - contenderKey.getLOD();
                        }
                    }
                }
            }
        }
    }

    if (normalMap)
    {
        // periodically check for cancelation
        if (progress && progress->isCanceled())
        {
            return false;
        }

        createNormalMap(key.getExtent(), hf, deltaLOD.get(), normalMap);
    }

#ifdef ANALYZE
    {
        static Threading::Mutex m;
        Threading::ScopedMutexLock lock(m);
        std::cout << key.str() << ": ";
        for (std::map<ElevationLayer*, LayerAnalysis>::const_iterator i = layerAnalysis.begin();
            i != layerAnalysis.end(); ++i)
        {
            std::cout << i->first->getName() 
                << " used=" << i->second.used
                << " failed=" << i->second.failed
                << " akv=" << i->second.actualKeyValid
                << " fallback=" << i->second.fallback
                << " samples=" << i->second.samples
                << " msg=" << i->second.message
                << "; ";
        }
        std::cout << std::endl;
    }
#endif

    if (progress && progress->isCanceled())
    {
        return false;
    }

    // Return whether or not we actually read any real data
    return realData;
}
コード例 #7
0
ファイル: ImageLayer.cpp プロジェクト: wqyun/osgearth
GeoImage
ImageLayer::assembleImageFromTileSource(const TileKey&    key,
                                        ProgressCallback* progress)
{
    GeoImage mosaicedImage, result;

    // Scale the extent if necessary to apply an "edge buffer"
    GeoExtent ext = key.getExtent();
    if ( _runtimeOptions.edgeBufferRatio().isSet() )
    {
        double ratio = _runtimeOptions.edgeBufferRatio().get();
        ext.scale(ratio, ratio);
    }

    // Get a set of layer tiles that intersect the requested extent.
    std::vector<TileKey> intersectingKeys;
    getProfile()->getIntersectingTiles( key, intersectingKeys );

    if ( intersectingKeys.size() > 0 )
    {
        double dst_minx, dst_miny, dst_maxx, dst_maxy;
        key.getExtent().getBounds(dst_minx, dst_miny, dst_maxx, dst_maxy);

        // if we find at least one "real" tile in the mosaic, then the whole result tile is
        // "real" (i.e. not a fallback tile)
        bool retry = false;
        ImageMosaic mosaic;

        // keep track of failed tiles.
        std::vector<TileKey> failedKeys;

        for( std::vector<TileKey>::iterator k = intersectingKeys.begin(); k != intersectingKeys.end(); ++k )
        {
            GeoImage image = createImageFromTileSource( *k, progress );

            if ( image.valid() )
            {
                if ( !isCoverage() )
                {
                    ImageUtils::fixInternalFormat(image.getImage());

                    // Make sure all images in mosaic are based on "RGBA - unsigned byte" pixels.
                    // This is not the smarter choice (in some case RGB would be sufficient) but
                    // it ensure consistency between all images / layers.
                    //
                    // The main drawback is probably the CPU memory foot-print which would be reduced by allocating RGB instead of RGBA images.
                    // On GPU side, this should not change anything because of data alignements : often RGB and RGBA textures have the same memory footprint
                    //
                    if (   (image.getImage()->getDataType() != GL_UNSIGNED_BYTE)
                        || (image.getImage()->getPixelFormat() != GL_RGBA) )
                    {
                        osg::ref_ptr<osg::Image> convertedImg = ImageUtils::convertToRGBA8(image.getImage());
                        if (convertedImg.valid())
                        {
                            image = GeoImage(convertedImg, image.getExtent());
                        }
                    }
                }

                mosaic.getImages().push_back( TileImage(image.getImage(), *k) );
            }
            else
            {
                // the tile source did not return a tile, so make a note of it.
                failedKeys.push_back( *k );

                if (progress && (progress->isCanceled() || progress->needsRetry()))
                {
                    retry = true;
                    break;
                }
            }
        }

        if ( mosaic.getImages().empty() || retry )
        {
            // if we didn't get any data, fail.
            OE_DEBUG << LC << "Couldn't create image for ImageMosaic " << std::endl;
            return GeoImage::INVALID;
        }

        // We got at least one good tile, so go through the bad ones and try to fall back on
        // lower resolution data to fill in the gaps. The entire mosaic must be populated or
        // this qualifies as a bad tile.
        for(std::vector<TileKey>::iterator k = failedKeys.begin(); k != failedKeys.end(); ++k)
        {
            GeoImage image;

            for(TileKey parentKey = k->createParentKey();
                parentKey.valid() && !image.valid();
                parentKey = parentKey.createParentKey())
            {
                image = createImageFromTileSource( parentKey, progress );
                if ( image.valid() )
                {
                    GeoImage cropped;

                    if ( !isCoverage() )
                    {
                        ImageUtils::fixInternalFormat(image.getImage());
                        if (   (image.getImage()->getDataType() != GL_UNSIGNED_BYTE)
                            || (image.getImage()->getPixelFormat() != GL_RGBA) )
                        {
                            osg::ref_ptr<osg::Image> convertedImg = ImageUtils::convertToRGBA8(image.getImage());
                            if (convertedImg.valid())
                            {
                                image = GeoImage(convertedImg, image.getExtent());
                            }
                        }

                        cropped = image.crop( k->getExtent(), false, image.getImage()->s(), image.getImage()->t() );
                    }

                    else
                    {
                        // TODO: may not work.... test; tilekey extent will <> cropped extent
                        cropped = image.crop( k->getExtent(), true, image.getImage()->s(), image.getImage()->t(), false );
                    }

                    // and queue it.
                    mosaic.getImages().push_back( TileImage(cropped.getImage(), *k) );       

                }
            }

            if ( !image.valid() )
            {
                // a tile completely failed, even with fallback. Eject.
                OE_DEBUG << LC << "Couldn't fallback on tiles for ImageMosaic" << std::endl;
                // let it go. The empty areas will be filled with alpha by ImageMosaic.
            }
        }

        // all set. Mosaic all the images together.
        double rxmin, rymin, rxmax, rymax;
        mosaic.getExtents( rxmin, rymin, rxmax, rymax );

        mosaicedImage = GeoImage(
            mosaic.createImage(),
            GeoExtent( getProfile()->getSRS(), rxmin, rymin, rxmax, rymax ) );
    }
    else
    {
        OE_DEBUG << LC << "assembleImageFromTileSource: no intersections (" << key.str() << ")" << std::endl;
    }

    // Final step: transform the mosaic into the requesting key's extent.
    if ( mosaicedImage.valid() )
    {
        // GeoImage::reproject() will automatically crop the image to the correct extents.
        // so there is no need to crop after reprojection. Also note that if the SRS's are the 
        // same (even though extents are different), then this operation is technically not a
        // reprojection but merely a resampling.

        result = mosaicedImage.reproject( 
            key.getProfile()->getSRS(),
            &key.getExtent(), 
            *_runtimeOptions.reprojectedTileSize(),
            *_runtimeOptions.reprojectedTileSize(),
            *_runtimeOptions.driver()->bilinearReprojection() );
    }

    // Process images with full alpha to properly support MP blending.
    if ( result.valid() && *_runtimeOptions.featherPixels() && !isCoverage() )
    {
        ImageUtils::featherAlphaRegions( result.getImage() );
    }

    return result;
}
コード例 #8
0
ファイル: ImageLayer.cpp プロジェクト: APerennec/osgearth
GeoImage
ImageLayer::createImageFromTileSource(const TileKey&    key,
                                      ProgressCallback* progress,
                                      bool              forceFallback,
                                      bool&             out_isFallback)
{
    // Results:
    // 
    // * return an osg::Image matching the key extent is all goes well;
    //
    // * return NULL to indicate that the key exceeds the maximum LOD of the source data,
    //   and that the engine may need to generate a "fallback" tile if necessary;
    //
    // deprecated:
    // * return an "empty image" if the LOD is valid BUT the key does not intersect the
    //   source's data extents.

    out_isFallback = false;

    TileSource* source = getTileSource();
    if ( !source )
        return GeoImage::INVALID;

    // If the profiles are different, use a compositing method to assemble the tile.
    if ( !key.getProfile()->isEquivalentTo( getProfile() ) )
    {
        return assembleImageFromTileSource( key, progress, out_isFallback );
    }

    // Good to go, ask the tile source for an image:
    osg::ref_ptr<TileSource::ImageOperation> op = _preCacheOp;

    osg::ref_ptr<osg::Image> result;

    if ( forceFallback )
    {
        // check if the tile source has any data coverage for the requested key.
        // the LOD is ignore here and checked later
        if ( !source->hasDataInExtent( key.getExtent() ) )
        {
            OE_DEBUG << LC << "createImageFromTileSource: hasDataInExtent(" << key.str() << ") == false" << std::endl;
            return GeoImage::INVALID;
        }

        TileKey finalKey = key;
        while( !result.valid() && finalKey.valid() )
        {
            if ( !source->getBlacklist()->contains( finalKey.getTileId() ) &&
                source->hasDataForFallback(finalKey))
            {
                result = source->createImage( finalKey, op.get(), progress );
                if ( result.valid() )
                {
                    if ( finalKey.getLevelOfDetail() != key.getLevelOfDetail() )
                    {
                        // crop the fallback image to match the input key, and ensure that it remains the
                        // same pixel size; because chances are if we're requesting a fallback that we're
                        // planning to mosaic it later, and the mosaicer requires same-size images.
                        GeoImage raw( result.get(), finalKey.getExtent() );
                        GeoImage cropped = raw.crop( key.getExtent(), true, raw.getImage()->s(), raw.getImage()->t(), *_runtimeOptions.driver()->bilinearReprojection() );
                        result = cropped.takeImage();
                    }
                }
            }
            if ( !result.valid() )
            {
                finalKey = finalKey.createParentKey();
                out_isFallback = true;
            }
        }

        if ( !result.valid() )
        {
            result = 0L;
            //result = _emptyImage.get();
            finalKey = key;
        }
    }
    else
    {
        // Fail is the image is blacklisted.
        if ( source->getBlacklist()->contains( key.getTileId() ) )
        {
            OE_DEBUG << LC << "createImageFromTileSource: blacklisted(" << key.str() << ")" << std::endl;
            return GeoImage::INVALID;
        }
    
        if ( !source->hasData( key ) )
        {
            OE_DEBUG << LC << "createImageFromTileSource: hasData(" << key.str() << ") == false" << std::endl;
            return GeoImage::INVALID;
        }
        result = source->createImage( key, op.get(), progress );
    }

    // Process images with full alpha to properly support MP blending.    
    if ( result != 0L && *_runtimeOptions.featherPixels())
    {
        ImageUtils::featherAlphaRegions( result.get() );
    }    
    
    // If image creation failed (but was not intentionally canceled),
    // blacklist this tile for future requests.
    if ( result == 0L && (!progress || !progress->isCanceled()) )
    {
        source->getBlacklist()->add( key.getTileId() );
    }

    return GeoImage(result.get(), key.getExtent());
}
コード例 #9
0
ファイル: ImageLayer.cpp プロジェクト: APerennec/osgearth
GeoImage
ImageLayer::createImageInNativeProfile( const TileKey& key, ProgressCallback* progress, bool forceFallback, bool& out_isFallback)
{
    out_isFallback = false;

    const Profile* nativeProfile = getProfile();
    if ( !nativeProfile )
    {
        OE_WARN << LC << "Could not establish the profile" << std::endl;
        return GeoImage::INVALID;
    }

    if ( key.getProfile()->isEquivalentTo(nativeProfile) )
    {
        // requested profile matches native profile, move along.
        return createImageInKeyProfile( key, progress, forceFallback, out_isFallback );
    }
    else
    {
        // find the intersection of keys.
        std::vector<TileKey> nativeKeys;
        nativeProfile->getIntersectingTiles(key.getExtent(), nativeKeys);


        //OE_INFO << "KEY = " << key.str() << ":" << std::endl;
        //for(int i=0; i<nativeKeys.size(); ++i)
        //    OE_INFO << "    " << nativeKeys[i].str() << std::endl;
        
        // build a mosaic of the images from the native profile keys:
        bool foundAtLeastOneRealTile = false;

        ImageMosaic mosaic;
        for( std::vector<TileKey>::iterator k = nativeKeys.begin(); k != nativeKeys.end(); ++k )
        {
            bool isFallback = false;
            GeoImage image = createImageInKeyProfile( *k, progress, true, isFallback );
            if ( image.valid() )
            {
                mosaic.getImages().push_back( TileImage(image.getImage(), *k) );
                if ( !isFallback )
                    foundAtLeastOneRealTile = true;
            }
            else
            {
                // if we get EVEN ONE invalid tile, we have to abort because there will be
                // empty spots in the mosaic. (By "invalid" we mean a tile that could not
                // even be resolved through the fallback procedure.)
                return GeoImage::INVALID;
            }
        }

        // bail out if we got nothing.
        if ( mosaic.getImages().size() == 0 )
            return GeoImage::INVALID;

        // if the mosaic is ALL fallback data, this tile is fallback data.
        if ( foundAtLeastOneRealTile )
        {
            // assemble new GeoImage from the mosaic.
            double rxmin, rymin, rxmax, rymax;
            mosaic.getExtents( rxmin, rymin, rxmax, rymax );

            GeoImage result( 
                mosaic.createImage(), 
                GeoExtent( nativeProfile->getSRS(), rxmin, rymin, rxmax, rymax ) );

#if 1
            return result;

#else // let's try this. why crop? Just leave it. Faster and more compatible with NPOT
      // systems (like iOS)

            // calculate a tigher extent that matches the original input key:
            GeoExtent tightExtent = nativeProfile->clampAndTransformExtent( key.getExtent() );

            // a non-exact crop is critical here to avoid resampling the data
            return result.crop( tightExtent, false, 0, 0, *_runtimeOptions.driver()->bilinearReprojection() );
#endif
        }

        else // all fallback data
        {
            GeoImage result;

            if ( forceFallback && key.getLevelOfDetail() > 0 )
            {
                result = createImageInNativeProfile(
                    key.createParentKey(),
                    progress,
                    forceFallback,
                    out_isFallback );
            }

            out_isFallback = true;
            return result;
        }

        //if ( !foundAtLeastOneRealTile )
        //    out_isFallback = true;

    }
}
コード例 #10
0
ファイル: ElevationLayer.cpp プロジェクト: Displacer/osgearth
bool
ElevationLayerVector::populateHeightField(osg::HeightField*      hf,
                                          const TileKey&         key,
                                          const Profile*         haeProfile,
                                          ElevationInterpolation interpolation,
                                          ProgressCallback*      progress ) const
{
    //osg::Timer_t startTime = osg::Timer::instance()->tick();
    // 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.
    LayerAndKeyVector contenders;
    LayerAndKeyVector offsets;

    // Track the number of layers that would return fallback data.
    unsigned numFallbackLayers = 0;

    // Check them in reverse order since the highest priority is last.
    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() );

            bool useLayer = true;
            TileKey bestKey( mappedKey );

            // Is there a tilesource? If not we are cache-only and cannot reject the layer.
            if ( layer->getTileSource() )
            {
                // Check whether the non-mapped key is valid according to the user's min/max level settings:
                if ( !layer->isKeyInRange(key) )
                {
                    useLayer = false;
                }
                

                // Find the "best available" mapped key from the tile source:
                else 
                {
                    if ( layer->getTileSource()->getBestAvailableTileKey(mappedKey, bestKey) )
                    {
                        // If the bestKey is not the mappedKey, this layer is providing
                        // fallback data (data at a lower resolution than requested)
                        if ( mappedKey != bestKey )
                        {
                            numFallbackLayers++;
                        }
                    }
                    else
                    {
                        useLayer = false;
                    }
                }
            }

            if ( useLayer )
            {
                if ( layer->isOffset() )
                {
                    offsets.push_back( std::make_pair(layer, bestKey) );
                }
                else
                {
                    contenders.push_back( std::make_pair(layer, bestKey) );
                }
            }
        }
    }

    // nothing? bail out.
    if ( contenders.empty() && offsets.empty() )
    {
        return false;
    }

    // if everything is fallback data, bail out.
    if ( contenders.size() + offsets.size() == numFallbackLayers )
    {
        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);

    // The maximum number of heightfields to keep in this local cache
    unsigned int maxHeightFields = 50;
    unsigned numHeightFieldsInCache = 0;

    //double fallBackTime = 0;

    const SpatialReference* keySRS = keyToUse.getProfile()->getSRS();

    bool realData = false;

    //unsigned int numFallback = 0;


    unsigned int total = numColumns * numRows;
    unsigned int completed = 0;

    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;

                ElevationLayer* layer = contenders[i].first.get();

                GeoHeightField& layerHF = heightFields[i];
                if ( !layerHF.valid() )
                {
                    layerHF = layer->createHeightField(contenders[i].second, progress);
                    
                    if ( !layerHF.valid() )
                    {
                        // This layer potentially has data or it wouldn't have ended up in the contendors list, so try falling back on the parent
                        TileKey parentKey = contenders[i].second.createParentKey();
                        while (!layerHF.valid() && parentKey.valid())
                        {
                            //numFallback++;
                            //osg::Timer_t fbStartTime = osg::Timer::instance()->tick();
                            GeoHeightField parentHF = layer->createHeightField(parentKey, progress);
                            //osg::Timer_t fbEndTime = osg::Timer::instance()->tick();

                            // Only penalize time wasted actually falling back.
                            //if (!parentHF.valid())
                            // {
                            //    fallBackTime += osg::Timer::instance()->delta_m(fbStartTime, fbEndTime);
                            //}

                            if (parentHF.valid())
                            {
                                layerHF = parentHF;
                                break;
                            }
                            else
                            {
                                parentKey = parentKey.createParentKey();
                            }

                        }

                        if (!layerHF.valid())
                        {
                            heightFailed[i] = true;
                            continue;
                        }
                    }
                    else
                    {
                        numHeightFieldsInCache++;
                    }
                }

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


                // Clear the heightfield cache if we have too many heightfields in the cache.
                if (numHeightFieldsInCache >= maxHeightFields)
                {
                    //OE_NOTICE << "Clearing cache" << std::endl;
                    for (unsigned int k = 0; k < heightFields.size(); k++)
                    {
                        heightFields[k] = GeoHeightField::INVALID;
                    }
                    numHeightFieldsInCache = 0;
                }
            }

            for(int i=offsets.size()-1; i>=0; --i)
            {
                if ( offsetFailed[i] )
                    continue;

                GeoHeightField& layerHF = offsetFields[i];
                if ( !layerHF.valid() )
                {
                    ElevationLayer* offset = offsets[i].first.get();

                    layerHF = offset->createHeightField(offsets[i].second, 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;
                }
            }

            completed++;
            //OE_NOTICE << "Completed " << completed << " of " << total << std::endl;
        }
    }   

    //osg::Timer_t endTime = osg::Timer::instance()->tick();
    //double totalTime = osg::Timer::instance()->delta_m(startTime, endTime);
   // double fallbackPercentage = fallBackTime / totalTime;
    //if (fallBackTime > 0)
    //{
    //    OE_NOTICE << "populateHeightField took " << totalTime << "ms fallbacktime=" << fallBackTime << "ms count=" << numFallback << " percentage=" << fallbackPercentage << std::endl;
    //}
    //else
    //{
    //    OE_NOTICE << "populateHeightField took " << totalTime << "ms" << std::endl;
    //}

    // Return whether or not we actually read any real data
    return realData;
}
コード例 #11
0
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();
}
コード例 #12
0
void
TileModelFactory::buildElevation(const TileKey&    key,
                                 const MapFrame&   frame,
                                 bool              accumulate,
                                 bool              buildTexture,
                                 TileModel*        model,
                                 ProgressCallback* progress)
{     
    const MapInfo& mapInfo = frame.getMapInfo();

    const osgEarth::ElevationInterpolation& interp =
        frame.getMapOptions().elevationInterpolation().get();

    // Request a heightfield from the map, falling back on lower resolution tiles
    // if necessary (fallback=true)
    osg::ref_ptr<osg::HeightField> hf;

    bool isFallback = false;

    // look up the parent's heightfield to use as a template
    osg::ref_ptr<osg::HeightField> parentHF;
    TileKey parentKey = key.createParentKey();
    if ( accumulate )
    {
        osg::ref_ptr<TileNode> parentNode;
        if (_liveTiles->get(parentKey, parentNode))
        {
            parentHF = parentNode->getTileModel()->_elevationData.getHeightField();
            if ( _debug && key.getLOD() > 0 && !parentHF.valid() )
            {
                OE_NOTICE << LC << "Could not find a parent tile HF for " << key.str() << "\n";
            }
        }
    }

    // Make a new heightfield:
    if (_meshHFCache->getOrCreateHeightField(frame, key, parentHF.get(), hf, isFallback, SAMPLE_FIRST_VALID, interp, progress))
    {
        model->_elevationData = TileModel::ElevationData(
            hf,
            GeoLocator::createForKey( key, mapInfo ),
            isFallback );

        // Edge normalization: requires adjacency information
        if ( _terrainOptions.normalizeEdges() == true )
        {
            for( int x=-1; x<=1; x++ )
            {
                for( int y=-1; y<=1; y++ )
                {
                    if ( x != 0 || y != 0 )
                    {
                        TileKey neighborKey = key.createNeighborKey(x, y);
                        if ( neighborKey.valid() )
                        {
                            osg::ref_ptr<osg::HeightField> neighborParentHF;
                            if ( accumulate )
                            {
                                TileKey neighborParentKey = neighborKey.createParentKey();
                                if (neighborParentKey == parentKey)
                                {
                                    neighborParentHF = parentHF;
                                }
                                else
                                {
                                    osg::ref_ptr<TileNode> neighborParentNode;
                                    if (_liveTiles->get(neighborParentKey, neighborParentNode))
                                    {
                                        neighborParentHF = neighborParentNode->getTileModel()->_elevationData.getHeightField();
                                    }
                                }
                            }

                            // only pull the tile if we have a valid parent HF for it -- otherwise
                            // you might get a flat tile when upsampling data.
                            if ( neighborParentHF.valid() )
                            {
                                osg::ref_ptr<osg::HeightField> hf;
                                if (_meshHFCache->getOrCreateHeightField(frame, neighborKey, neighborParentHF.get(), hf, isFallback, SAMPLE_FIRST_VALID, interp, progress) )
                                {
                                    model->_elevationData.setNeighbor( x, y, hf.get() );
                                }
                            }
                        }
                    }
                }
            }

            // parent too.
            if ( parentHF.valid() )
            {
                model->_elevationData.setParent( parentHF.get() );
            }
        }

        if ( buildTexture )
        {
            model->generateElevationTexture();
        }
    }
}
コード例 #13
0
ファイル: ElevationQuery.cpp プロジェクト: aashish24/osgearth
bool
ElevationQuery::getElevationImpl(const GeoPoint& point,
                                 double&         out_elevation,
                                 double          desiredResolution,
                                 double*         out_actualResolution)
{
    osg::Timer_t start = osg::Timer::instance()->tick();

    if ( _mapf.elevationLayers().empty() )
    {
        // this means there are no heightfields.
        out_elevation = 0.0;
        return true;        
    }

    // tile size (resolution of elevation tiles)
    unsigned tileSize = std::max(_mapf.getMapOptions().elevationTileSize().get(), 2u);

    //This is the max resolution that we actually have data at this point
    unsigned int bestAvailLevel = getMaxLevel( point.x(), point.y(), point.getSRS(), _mapf.getProfile());

    if (desiredResolution > 0.0)
    {
        unsigned int desiredLevel = _mapf.getProfile()->getLevelOfDetailForHorizResolution( desiredResolution, tileSize );
        if (desiredLevel < bestAvailLevel) bestAvailLevel = desiredLevel;
    }

    OE_DEBUG << LC << "Best available data level " << point.x() << ", " << point.y() << " = "  << bestAvailLevel << std::endl;

    // transform the input coords to map coords:
    GeoPoint mapPoint = point;
    if ( point.isValid() && !point.getSRS()->isHorizEquivalentTo( _mapf.getProfile()->getSRS() ) )
    {
        mapPoint = point.transform(_mapf.getProfile()->getSRS());
        if ( !mapPoint.isValid() )
        {
            OE_WARN << LC << "Fail: coord transform failed" << std::endl;
            return false;
        }
    }    

    // get the tilekey corresponding to the tile we need:
    TileKey key = _mapf.getProfile()->createTileKey( mapPoint.x(), mapPoint.y(), bestAvailLevel );
    if ( !key.valid() )
    {
        OE_WARN << LC << "Fail: coords fall outside map" << std::endl;
        return false;
    }
        
    bool result = false;      
    while (!result)
    {      
        GeoHeightField geoHF;
        TileCache::Record record;
        // Try to get the hf from the cache
        if ( _cache.get( key, record ) )
        {                        
            geoHF = record.value();
        }
        else
        {
            // Create it            
            osg::ref_ptr<osg::HeightField> hf = new osg::HeightField();
            hf->allocate( tileSize, tileSize );

            // Initialize the heightfield to nodata
            for (unsigned int i = 0; i < hf->getFloatArray()->size(); i++)
            {
                hf->getFloatArray()->at( i ) = NO_DATA_VALUE;
            }   

            if (_mapf.populateHeightField( hf, key ) )
            {                
                geoHF = GeoHeightField( hf.get(), key.getExtent() );
                _cache.insert( key, geoHF );
            }
        }

        if (geoHF.valid())
        {            
            float elevation = 0.0f;                 
            result = geoHF.getElevation( mapPoint.getSRS(), mapPoint.x(), mapPoint.y(), _mapf.getMapInfo().getElevationInterpolation(), mapPoint.getSRS(), elevation);                              
            if (result && elevation != NO_DATA_VALUE)
            {                        
                // see what the actual resolution of the heightfield is.
                if ( out_actualResolution )
                    *out_actualResolution = geoHF.getXInterval(); 
                out_elevation = (double)elevation;                
                break;
            }
            else
            {                               
                result = false;
            }
        }

        if (!result)
        {
            key = key.createParentKey();                        
            if (!key.valid())
            {
                break;
            }
        }         
    }

         

    osg::Timer_t end = osg::Timer::instance()->tick();
    _queries++;
    _totalTime += osg::Timer::instance()->delta_s( start, end );

    return result;
}
コード例 #14
0
ファイル: ElevationLayer.cpp プロジェクト: JohnDr/osgearth
bool
ElevationLayerVector::createHeightField(const TileKey&                  key,
                                        bool                            fallback,
                                        const Profile*                  haeProfile,
                                        ElevationInterpolation          interpolation,
                                        ElevationSamplePolicy           samplePolicy,
                                        osg::ref_ptr<osg::HeightField>& out_result,
                                        bool*                           out_isFallback,
                                        ProgressCallback*               progress )  const
{        
    unsigned lowestLOD = key.getLevelOfDetail();
    bool hfInitialized = false;

    //Get a HeightField for each of the enabled layers
    GeoHeightFieldVector heightFields;

    //The number of fallback heightfields we have
    int numFallbacks = 0;

    //Default to being fallback data.
    if ( out_isFallback )
    {
        *out_isFallback = true;
    }

    // if the caller provided an "HAE map profile", he wants an HAE elevation grid even if
    // the map profile has a vertical datum. This is the usual case when building the 3D
    // terrain, for example. Construct a temporary key that doesn't have the vertical
    // datum info and use that to query the elevation data.
    TileKey keyToUse = key;
    if ( haeProfile )
    {
        keyToUse = TileKey(key.getLevelOfDetail(), key.getTileX(), key.getTileY(), haeProfile );
    }

    // Generate a heightfield for each elevation layer.

    unsigned defElevSize = 8;

    for( ElevationLayerVector::const_iterator i = this->begin(); i != this->end(); i++ )
    {
        ElevationLayer* layer = i->get();
        if ( layer->getVisible() )
        {
            GeoHeightField geoHF = layer->createHeightField( keyToUse, progress );

            // if "fallback" is set, try to fall back on lower LODs.
            if ( !geoHF.valid() && fallback )
            {
                TileKey hf_key = keyToUse.createParentKey();

                while ( hf_key.valid() && !geoHF.valid() )
                {
                    geoHF = layer->createHeightField( hf_key, progress );
                    if ( !geoHF.valid() )
                        hf_key = hf_key.createParentKey();
                }

                if ( geoHF.valid() )
                {
                    if ( hf_key.getLevelOfDetail() < lowestLOD )
                        lowestLOD = hf_key.getLevelOfDetail();

                    //This HeightField is fallback data, so increment the count.
                    numFallbacks++;
                }
            }

            if ( geoHF.valid() )
            {
                heightFields.push_back( geoHF );
            }
        }
    }

    //If any of the layers produced valid data then it's not considered a fallback
    if ( out_isFallback )
    {
        *out_isFallback = (numFallbacks == heightFields.size());
        //OE_NOTICE << "Num fallbacks=" << numFallbacks << " numHeightFields=" << heightFields.size() << " is fallback " << *out_isFallback << std::endl;
    }   

    if ( heightFields.size() == 0 )
    {            
        //If we got no heightfields but were requested to fallback, create an empty heightfield.
        if ( fallback )
        {
            out_result = HeightFieldUtils::createReferenceHeightField( keyToUse.getExtent(), defElevSize, defElevSize );                
            return true;
        }
        else
        {
            //We weren't requested to fallback so just return.
            return false;
        }
    }

    else if (heightFields.size() == 1)
    {
        if ( lowestLOD == key.getLevelOfDetail() )
        {
            //If we only have on heightfield, just return it.
            out_result = heightFields[0].takeHeightField();
        }
        else
        {
            GeoHeightField geoHF = heightFields[0].createSubSample( key.getExtent(), interpolation);
            out_result = geoHF.takeHeightField();
            hfInitialized = true;
        }
    }

    else
    {
        //If we have multiple heightfields, we need to composite them together.
        unsigned int width = 0;
        unsigned int height = 0;

        for (GeoHeightFieldVector::const_iterator i = heightFields.begin(); i < heightFields.end(); ++i)
        {
            if (i->getHeightField()->getNumColumns() > width) 
                width = i->getHeightField()->getNumColumns();
            if (i->getHeightField()->getNumRows() > height) 
                height = i->getHeightField()->getNumRows();
        }
        out_result = new osg::HeightField();
        out_result->allocate( width, height );

        //Go ahead and set up the heightfield so we don't have to worry about it later
        double minx, miny, maxx, maxy;
        key.getExtent().getBounds(minx, miny, maxx, maxy);
        double dx = (maxx - minx)/(double)(out_result->getNumColumns()-1);
        double dy = (maxy - miny)/(double)(out_result->getNumRows()-1);

        const SpatialReference* keySRS = keyToUse.getProfile()->getSRS();

        //Create the new heightfield by sampling all of them.
        for (unsigned int c = 0; c < width; ++c)
        {
            double x = minx + (dx * (double)c);
            for (unsigned r = 0; r < height; ++r)
            {
                double y = miny + (dy * (double)r);

                //Collect elevations from all of the layers. Iterate BACKWARDS because the last layer
                // is the highest priority.
                std::vector<float> elevations;
                for( GeoHeightFieldVector::reverse_iterator itr = heightFields.rbegin(); itr != heightFields.rend(); ++itr )
                {
                    const GeoHeightField& geoHF = *itr;

                    float elevation = 0.0f;
                    if ( geoHF.getElevation(keySRS, x, y, interpolation, keySRS, elevation) )
                    {
                        if (elevation != NO_DATA_VALUE)
                        {
                            elevations.push_back(elevation);
                        }
                    }
                }

                float elevation = NO_DATA_VALUE;

                //The list of elevations only contains valid values
                if (elevations.size() > 0)
                {
                    if (samplePolicy == SAMPLE_FIRST_VALID)
                    {
                        elevation = elevations[0];
                    }
                    else if (samplePolicy == SAMPLE_HIGHEST)
                    {
                        elevation = -FLT_MAX;
                        for (unsigned int i = 0; i < elevations.size(); ++i)
                        {
                            if (elevation < elevations[i]) elevation = elevations[i];
                        }
                    }
                    else if (samplePolicy == SAMPLE_LOWEST)
                    {
                        elevation = FLT_MAX;
                        for (unsigned i = 0; i < elevations.size(); ++i)
                        {
                            if (elevation > elevations[i]) elevation = elevations[i];
                        }
                    }
                    else if (samplePolicy == SAMPLE_AVERAGE)
                    {
                        elevation = 0.0;
                        for (unsigned i = 0; i < elevations.size(); ++i)
                        {
                            elevation += elevations[i];
                        }
                        elevation /= (float)elevations.size();
                    }
                }
                out_result->setHeight(c, r, elevation);
            }
        }
    }

    // Replace any NoData areas with the reference value. This is zero for HAE datums,
    // and some geoid height for orthometric datums.
    if (out_result.valid())
    {
        const Geoid*         geoid = 0L;
        const VerticalDatum* vdatum = key.getProfile()->getSRS()->getVerticalDatum();

        if ( haeProfile && vdatum )
        {
            geoid = vdatum->getGeoid();
        }

        HeightFieldUtils::resolveInvalidHeights(
            out_result.get(),
            key.getExtent(),
            NO_DATA_VALUE,
            geoid );

        //ReplaceInvalidDataOperator o;
        //o.setValidDataOperator(new osgTerrain::NoDataValue(NO_DATA_VALUE));
        //o( out_result.get() );
    }

    //Initialize the HF values for osgTerrain
    if (out_result.valid() && !hfInitialized )
    {   
        //Go ahead and set up the heightfield so we don't have to worry about it later
        double minx, miny, maxx, maxy;
        key.getExtent().getBounds(minx, miny, maxx, maxy);
        out_result->setOrigin( osg::Vec3d( minx, miny, 0.0 ) );
        double dx = (maxx - minx)/(double)(out_result->getNumColumns()-1);
        double dy = (maxy - miny)/(double)(out_result->getNumRows()-1);
        out_result->setXInterval( dx );
        out_result->setYInterval( dy );
        out_result->setBorderWidth( 0 );
    }

    return out_result.valid();
}
コード例 #15
0
ファイル: ElevationLayer.cpp プロジェクト: JohnDr/osgearth
osg::HeightField*
ElevationLayer::assembleHeightFieldFromTileSource(const TileKey&    key,
                                                  ProgressCallback* progress)
{			
    osg::HeightField* result = 0L;

    // Collect the heightfields for each of the intersecting tiles.
    GeoHeightFieldVector heightFields;

    //Determine the intersecting keys
    std::vector< TileKey > intersectingTiles;
    getProfile()->getIntersectingTiles( key, intersectingTiles );

    // collect heightfield for each intersecting key. Note, we're hitting the
    // underlying tile source here, so there's no vetical datum shifts happening yet.
    // we will do that later.
    if ( intersectingTiles.size() > 0 )
    {
        for (unsigned int i = 0; i < intersectingTiles.size(); ++i)
        {
            const TileKey& layerKey = intersectingTiles[i];

            if ( isKeyValid(layerKey) )
            {
                osg::HeightField* hf = createHeightFieldFromTileSource( layerKey, progress );
                if ( hf )
                {
                    heightFields.push_back( GeoHeightField(hf, layerKey.getExtent()) );
                }
                else
                { 
                    //We couldn't get a heightfield at the given key so fall back on parent tiles
                    TileKey parentKey = layerKey.createParentKey();
                    while (!hf && parentKey.valid())
                    {
                        hf = createHeightFieldFromTileSource( parentKey, progress );
                        if (hf)
                        {
                            heightFields.push_back( GeoHeightField(hf, parentKey.getExtent()) );
                            break;
                        }                        
                        parentKey = parentKey.createParentKey();
                    }                    
                }
            }
        }
    }

    // If we actually got a HeightField, resample/reproject it to match the incoming TileKey's extents.
    if (heightFields.size() > 0)
    {		
        unsigned int width = 0;
        unsigned int height = 0;

        for (GeoHeightFieldVector::iterator itr = heightFields.begin(); itr != heightFields.end(); ++itr)
        {
            if (itr->getHeightField()->getNumColumns() > width)
                width = itr->getHeightField()->getNumColumns();
            if (itr->getHeightField()->getNumRows() > height) 
                height = itr->getHeightField()->getNumRows();
        }

        result = new osg::HeightField();
        result->allocate(width, height);

        //Go ahead and set up the heightfield so we don't have to worry about it later
        double minx, miny, maxx, maxy;
        key.getExtent().getBounds(minx, miny, maxx, maxy);
        double dx = (maxx - minx)/(double)(width-1);
        double dy = (maxy - miny)/(double)(height-1);

        //Create the new heightfield by sampling all of them.
        for (unsigned int c = 0; c < width; ++c)
        {
            double x = minx + (dx * (double)c);
            for (unsigned r = 0; r < height; ++r)
            {
                double y = miny + (dy * (double)r);

                //For each sample point, try each heightfield.  The first one with a valid elevation wins.
                float elevation = NO_DATA_VALUE;
                for (GeoHeightFieldVector::iterator itr = heightFields.begin(); itr != heightFields.end(); ++itr)
                {
                    // get the elevation value, at the same time transforming it vertically into the 
                    // requesting key's vertical datum.
                    float e = 0.0;
                    if (itr->getElevation(key.getExtent().getSRS(), x, y, INTERP_BILINEAR, key.getExtent().getSRS(), e))
                    {
                        elevation = e;
                        break;
                    }
                }
                result->setHeight( c, r, elevation );                
            }
        }
    }

    return result;
}
コード例 #16
0
ファイル: TileModelFactory.cpp プロジェクト: JohnDr/osgearth
void
TileModelFactory::createTileModel(const TileKey&           key, 
                                  osg::ref_ptr<TileModel>& out_model,
                                  bool&                    out_hasRealData,
                                  bool&                    out_hasLodBlendedLayers )
{
    MapFrame mapf( _map, Map::MASKED_TERRAIN_LAYERS );
    
    const MapInfo& mapInfo = mapf.getMapInfo();

    osg::ref_ptr<TileModel> model = new TileModel();
    model->_tileKey = key;
    model->_tileLocator = GeoLocator::createForKey(key, mapInfo);

    // init this to false, then search for real data. "Real data" is data corresponding
    // directly to the key, as opposed to fallback data, which is derived from a lower
    // LOD key.
    out_hasRealData = false;
    out_hasLodBlendedLayers = false;
    
    // Fetch the image data and make color layers.
    for( ImageLayerVector::const_iterator i = mapf.imageLayers().begin(); i != mapf.imageLayers().end(); ++i )
    {
        ImageLayer* layer = i->get();

        if ( layer->getEnabled() )
        {
            BuildColorData build;
            build.init( key, layer, mapInfo, _terrainOptions, model.get() );
            build.execute();

            if ( layer->getImageLayerOptions().lodBlending() == true )
            {
                out_hasLodBlendedLayers = true;
            }
        }
    }

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


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

    // OK we are making a tile, so if there's no heightfield yet, make an empty one.
    if ( !model->_elevationData.getHFLayer() )
    {
        osg::HeightField* hf = HeightFieldUtils::createReferenceHeightField( key.getExtent(), 8, 8 );
        osgTerrain::HeightFieldLayer* hfLayer = new osgTerrain::HeightFieldLayer( hf );
        hfLayer->setLocator( GeoLocator::createForKey(key, mapInfo) );
        model->_elevationData = TileModel::ElevationData( hfLayer, true );
    }

    // Now, if there are any color layers that did not get built, create them with an empty
    // image so the shaders have something to draw.
    osg::ref_ptr<osg::Image> emptyImage;
    osgTerrain::Locator* locator = model->_elevationData.getHFLayer()->getLocator();

    for( ImageLayerVector::const_iterator i = mapf.imageLayers().begin(); i != mapf.imageLayers().end(); ++i )
    {
        ImageLayer* layer = i->get();

        if ( layer->getEnabled() && !layer->isKeyValid(key) )
        {
            if ( !emptyImage.valid() )
                emptyImage = ImageUtils::createEmptyImage();

            model->_colorData[i->get()->getUID()] = TileModel::ColorData(
                layer,
                emptyImage.get(),
                locator,
                key.getLevelOfDetail(),
                key,
                true );
        }
    }

    // Ready to create the actual tile.
    //AssembleTile assemble;
    //assemble.init( key, mapInfo, _terrainOptions, model.get(), mapf.terrainMaskLayers() );
    //assemble.execute();

    // if we're using LOD blending, find and add the parent's state set.
    if ( out_hasLodBlendedLayers && key.getLevelOfDetail() > 0 && _liveTiles.valid() )
    {
        osg::ref_ptr<TileNode> parent;
        if ( _liveTiles->get( key.createParentKey(), parent ) )
        {
            model->_parentStateSet = parent->getPublicStateSet();
        }
    }

    if (!out_hasRealData)
    {
        // Check the results and see if we have any real data.
        for( TileModel::ColorDataByUID::const_iterator i = model->_colorData.begin(); i != model->_colorData.end(); ++i )
        {
            if ( !i->second.isFallbackData() ) 
            {
                out_hasRealData = true;
                break;
            }
        }
    }

    if ( !out_hasRealData && !model->_elevationData.isFallbackData() )
    {
        out_hasRealData = true;
    }

    out_model = model.release();
    //out_tile = assemble._node;
}
コード例 #17
0
ファイル: OSGTileFactory.cpp プロジェクト: hulumogu/osgearth
osg::Node*
OSGTileFactory::createPlaceholderTile(const MapFrame&   mapf,
                                      StreamingTerrain* terrain,
                                      const TileKey&    key )
{
    // Start out by finding the nearest registered ancestor tile, since the placeholder is
    // going to be based on inherited data. Note- the ancestor may not be the immediate
    // parent, b/c the parent may or may not be in the scene graph.
    TileKey ancestorKey = key.createParentKey();
    osg::ref_ptr<StreamingTile> ancestorTile;
    while( !ancestorTile.valid() && ancestorKey.valid() )
    {
        terrain->getTile( ancestorKey.getTileId(), ancestorTile );
        if ( !ancestorTile.valid() )
            ancestorKey = ancestorKey.createParentKey();
    }
    if ( !ancestorTile.valid() )
    {
        OE_WARN << LC << "cannot find ancestor tile for (" << key.str() << ")" <<std::endl;
        return 0L;
    }

    OE_DEBUG << LC << "Creating placeholder for " << key.str() << std::endl;

    const MapInfo& mapInfo = mapf.getMapInfo();

    bool hasElevation = mapf.elevationLayers().size() > 0;

    // Build a "placeholder" tile.
    double xmin, ymin, xmax, ymax;
    key.getExtent().getBounds( xmin, ymin, xmax, ymax );

    // A locator will place the tile on the globe:
    osg::ref_ptr<GeoLocator> locator = GeoLocator::createForKey( key, mapInfo );

    // The empty tile:
    StreamingTile* tile = new StreamingTile( key, locator.get(), terrain->getQuickReleaseGLObjects() );
    tile->setTerrainTechnique( terrain->cloneTechnique() );
    tile->setVerticalScale( _terrainOptions.verticalScale().value() );
    tile->setDataVariance( osg::Object::DYNAMIC );
    //tile->setLocator( locator.get() );

    // Attach an updatecallback to normalize the edges of TerrainTiles.
#if 0
    if ( hasElevation && _terrainOptions.normalizeEdges().get() )
    {
        tile->setUpdateCallback(new TerrainTileEdgeNormalizerUpdateCallback());
        tile->setDataVariance(osg::Object::DYNAMIC);
    }
#endif

    // Generate placeholder imagery and elevation layers. These "inherit" data from an
    // ancestor tile.
    {
        //Threading::ScopedReadLock parentLock( ancestorTile->getTileLayersMutex() );
        addPlaceholderImageLayers     ( tile, ancestorTile.get() );
        addPlaceholderHeightfieldLayer( tile, ancestorTile.get(), locator.get(), key, ancestorKey );
    }

    // calculate the switching distances:
    osg::BoundingSphere bs = tile->getBound();
    double max_range = 1e10;
    double radius = bs.radius();
    double min_range = radius * _terrainOptions.minTileRangeFactor().get();

    // Set the skirt height of the heightfield
    osgTerrain::HeightFieldLayer* hfLayer = static_cast<osgTerrain::HeightFieldLayer*>(tile->getElevationLayer());
    if (!hfLayer)
    {
        OE_WARN << LC << "Warning: Couldn't get hfLayer for " << key.str() << std::endl;
    }
    hfLayer->getHeightField()->setSkirtHeight(radius * _terrainOptions.heightFieldSkirtRatio().get() );

    // In a Plate Carre tesselation, scale the heightfield elevations from meters to degrees
    if ( mapInfo.isPlateCarre() && hfLayer->getHeightField() )
        HeightFieldUtils::scaleHeightFieldToDegrees( hfLayer->getHeightField() );

    bool markTileLoaded = false;

    if ( _terrainOptions.loadingPolicy()->mode().get() != LoadingPolicy::MODE_STANDARD )
    {
        markTileLoaded = true;
        tile->setHasElevationHint( hasElevation );
    }

    // install a tile switcher:
    tile->attachToTerrain( terrain );
    //tile->setTerrain( terrain );
    //terrain->registerTile( tile );

    osg::Node* result = 0L;

    // create a PLOD so we can keep subdividing:
    osg::PagedLOD* plod = new osg::PagedLOD();
    plod->setCenter( bs.center() );
    plod->addChild( tile, min_range, max_range );

    if ( key.getLevelOfDetail() < (unsigned int)getTerrainOptions().maxLOD().get() )
    {
        plod->setFileName( 1, createURI( _engineId, key ) ); //map->getId(), key ) );
        plod->setRange( 1, 0.0, min_range );
    }
    else
    {
        plod->setRange( 0, 0, FLT_MAX );
    }

#if 0 //USE_FILELOCATIONCALLBACK
    osgDB::Options* options = new osgDB::Options;
    options->setFileLocationCallback( new FileLocationCallback);
    plod->setDatabaseOptions( options );
#endif

    result = plod;

    // Install a callback that will load the actual tile data via the pager.
    result->addCullCallback( new PopulateStreamingTileDataCallback( _cull_thread_mapf ) );

    // Install a cluster culler (FIXME for cube mode)
    //bool isCube = map->getMapOptions().coordSysType() == MapOptions::CSTYPE_GEOCENTRIC_CUBE;
    if ( mapInfo.isGeocentric() && !mapInfo.isCube() )
    {
        osg::ClusterCullingCallback* ccc = createClusterCullingCallback( tile, locator->getEllipsoidModel() );
        result->addCullCallback( ccc );
    }     

    return result;
}
コード例 #18
0
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;
}
コード例 #19
0
void
TextureCompositorTexArray::applyLayerUpdate(osg::StateSet*       stateSet,
                                            UID                  layerUID,
                                            const GeoImage&      preparedImage,
                                            const TileKey&       tileKey,
                                            const TextureLayout& layout,
                                            osg::StateSet*       parentStateSet) const
{
    GeoExtent tileExtent(tileKey.getExtent());
    int slot = layout.getSlot( layerUID );
    if ( slot < 0 )
        return; // means the layer no longer exists

    // access the texture array, creating or growing it if necessary:
    osg::Texture2DArray* texture = s_getTexture( stateSet, layout, 0,
                                                 textureSize() );
    ensureSampler( stateSet, 0 );
    // assign the new image at the proper position in the texture array.
    osg::Image* image = preparedImage.getImage();
    assignImage(texture, slot, image);
    
    // update the region uniform to reflect the geo extent of the image:
    const GeoExtent& imageExtent = preparedImage.getExtent();
    osg::Vec4 tileTransform;
    getImageTransform(tileExtent, imageExtent, tileTransform);

    // access the region uniform, creating or growing it if necessary:
    ArrayUniform regionUni( "region", osg::Uniform::FLOAT, stateSet, layout.getMaxUsedSlot()+1 );
    if ( regionUni.isValid() )
    {
        int layerOffset = slot * 8;
        for (int i = 0; i < 4; ++i)
            regionUni.setElement( layerOffset + i, tileTransform[i]);
        //region->dirty();
    }
    
    if ( layout.isBlendingEnabled( layerUID ) && regionUni.isValid() )
    {
        osg::Uniform* secondarySampler = ensureSampler( stateSet, 1 );
        osg::Texture2DArray* parentTexture = 0;
        const unsigned parentLayerOffset = slot * 8 + 4;
        if ( parentStateSet )
        {
            ArrayUniform parentRegion( "region", osg::Uniform::FLOAT, parentStateSet, layout.getMaxUsedSlot()+1 );

            //osg::Uniform* parentRegion = s_getRegionUniform( parentStateSet,
            //                                                 layout );
            GeoExtent parentExtent(tileKey.createParentKey().getExtent());
            float widthRatio, heightRatio;
            parentRegion.getElement(slot * 8 + 2, widthRatio);
            parentRegion.getElement(slot * 8 + 3, heightRatio);
            float parentImageWidth =  parentExtent.width() / widthRatio;
            float parentImageHeight = parentExtent.height() / heightRatio;
            float xRatio, yRatio;
            parentRegion.getElement(slot * 8, xRatio);
            parentRegion.getElement(slot * 8 + 1, yRatio);
            float ParentImageXmin = parentExtent.xMin() - xRatio * parentImageWidth;
            float ParentImageYmin = parentExtent.yMin() - yRatio * parentImageHeight;
            regionUni.setElement(parentLayerOffset,
                               static_cast<float>((tileExtent.xMin() - ParentImageXmin) / parentImageWidth));
            regionUni.setElement(parentLayerOffset + 1,
                               static_cast<float>((tileExtent.yMin() - ParentImageYmin) / parentImageHeight));
            regionUni.setElement(parentLayerOffset + 2,
                               static_cast<float>(tileExtent.width() / parentImageWidth));
            regionUni.setElement(parentLayerOffset + 3,
                               static_cast<float>(tileExtent.height() / parentImageHeight));
            //regionUni.dirty();
            parentTexture = static_cast<osg::Texture2DArray*>(parentStateSet->getTextureAttribute(0, osg::StateAttribute::TEXTURE));
        }
        else
        {
            // setting the parent transform values to -1 disabled blending for this layer. #hack -gw
            for (int i = 0; i < 4; ++i)
                regionUni.setElement(parentLayerOffset + i, tileTransform[i]);
        }

        if (parentTexture)
            stateSet->setTextureAttribute(1, parentTexture, osg::StateAttribute::ON);
        else
            secondarySampler->set(0);

        // update the timestamp on the image layer to support fade-in blending.
        float now = (float)osg::Timer::instance()->delta_s( osg::Timer::instance()->getStartTick(), osg::Timer::instance()->tick() );
        ArrayUniform stampUniform( "osgearth_SlotStamp", osg::Uniform::FLOAT, stateSet, layout.getMaxUsedSlot()+1 );
        stampUniform.setElement( slot, now );
    }
}
コード例 #20
0
ファイル: HeightFieldCache.cpp プロジェクト: Geo12/osgearth
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;
}
コード例 #21
0
osg::Image*
CompositeTileSource::createImage(const TileKey&    key,
                                 ProgressCallback* progress )
{    
    ImageMixVector images;
    images.reserve(_imageLayers.size());

    // Try to get an image from each of the layers for the given key.
    for (ImageLayerVector::const_iterator itr = _imageLayers.begin(); itr != _imageLayers.end(); ++itr)
    {
        ImageLayer* layer = itr->get();
        ImageInfo imageInfo;
        imageInfo.dataInExtents = layer->getTileSource()->hasDataInExtent( key.getExtent() );
        imageInfo.opacity = layer->getOpacity();

        if (imageInfo.dataInExtents)
        {
            GeoImage image = layer->createImage(key, progress);
            if (image.valid())
            {
                imageInfo.image = image.getImage();
            }

            // If the progress got cancelled or it needs a retry then return NULL to prevent this tile from being built and cached with incomplete or partial data.
            if (progress && (progress->isCanceled() || progress->needsRetry()))
            {
                OE_DEBUG << LC << " createImage was cancelled or needs retry for " << key.str() << std::endl;
                return 0L;
            }
        }

        images.push_back(imageInfo);
    }

    // Determine the output texture size to use based on the image that were creatd.
    unsigned numValidImages = 0;
    osg::Vec2s textureSize;
    for (unsigned int i = 0; i < images.size(); i++)
    {
        ImageInfo& info = images[i];
        if (info.image.valid())
        {
            if (numValidImages == 0)
            {
                textureSize.set( info.image->s(), info.image->t());
            }
            numValidImages++;        
        }
    } 

    // Create fallback images if we have some valid data but not for all the layers
    if (numValidImages > 0 && numValidImages < images.size())
    {
        for (unsigned int i = 0; i < images.size(); i++)
        {
            ImageInfo& info = images[i];
            ImageLayer* layer = _imageLayers[i].get();
            if (!info.image.valid() && info.dataInExtents)
            {                      
                TileKey parentKey = key.createParentKey();

                GeoImage image;
                while (!image.valid() && parentKey.valid())
                {
                    image = layer->createImage(parentKey, progress);
                    if (image.valid())
                    {
                        break;
                    }

                    // If the progress got cancelled or it needs a retry then return NULL to prevent this tile from being built and cached with incomplete or partial data.
                    if (progress && (progress->isCanceled() || progress->needsRetry()))
                    {
                        OE_DEBUG << LC << " createImage was cancelled or needs retry for " << key.str() << std::endl;
                        return 0L;
                    }

                    parentKey = parentKey.createParentKey();
                }

                if (image.valid())
                {                                        
                    // TODO:  Bilinear options?
                    bool bilinear = layer->isCoverage() ? false : true;
                    GeoImage cropped = image.crop( key.getExtent(), true, textureSize.x(), textureSize.y(), bilinear);
                    info.image = cropped.getImage();
                }                    
            }
        }
    }

    // Now finally create the output image.
    //Recompute the number of valid images
    numValidImages = 0;
    for (unsigned int i = 0; i < images.size(); i++)
    {
        ImageInfo& info = images[i];
        if (info.image.valid()) numValidImages++;        
    }    

    if ( progress && progress->isCanceled() )
    {
        return 0L;
    }
    else if ( numValidImages == 0 )
    {
        return 0L;
    }
    else if ( numValidImages == 1 )
    {
        //We only have one valid image, so just return it and don't bother with compositing
        for (unsigned int i = 0; i < images.size(); i++)
        {
            ImageInfo& info = images[i];
            if (info.image.valid())
                return info.image.release();
        }
        return 0L;
    }
    else
    {
        osg::Image* result = 0;
        for (unsigned int i = 0; i < images.size(); i++)
        {
            ImageInfo& imageInfo = images[i];
            if (!result)
            {
                if (imageInfo.image.valid())
                {
                    result = new osg::Image( *imageInfo.image.get());
                }
            }
            else
            {
                if (imageInfo.image.valid())
                {
                    ImageUtils::mix( result, imageInfo.image.get(), imageInfo.opacity );
                }
            }            
        }        
        return result;
    }



}
コード例 #22
0
void
TileModelFactory::buildNormalMap(const TileKey&    key,
                                 const MapFrame&   frame,
                                 bool              accumulate,
                                 TileModel*        model,
                                 ProgressCallback* progress)
{   
    const MapInfo& mapInfo = frame.getMapInfo();

    const osgEarth::ElevationInterpolation& interp =
        frame.getMapOptions().elevationInterpolation().get();

    // Request a heightfield from the map, falling back on lower resolution tiles
    // if necessary (fallback=true)
    osg::ref_ptr<osg::HeightField> hf;
    osg::ref_ptr<osg::HeightField> parentHF;
    osg::ref_ptr<const TileModel>  parentModel;

    bool isFallback = false;

    unsigned minNormalLOD =
        _terrainOptions.minNormalMapLOD().isSet() ?
        _terrainOptions.minNormalMapLOD().get() : 0u;

    if ( key.getLOD() >= minNormalLOD )
    {
        // look up the parent's heightfield to use as a template
    
        TileKey parentKey = key.createParentKey();
        if ( accumulate )
        {
            osg::ref_ptr<TileNode> parentNode;
            if (_liveTiles->get(parentKey, parentNode))
            {
                parentModel = parentNode->getTileModel();
                parentHF = parentModel->_normalData.getHeightField();
                if ( parentHF->getNumColumns() == EMPTY_NORMAL_MAP_SIZE )
                    parentHF = 0L;
            }
        }

        // Make a new heightfield:
        if (_normalHFCache->getOrCreateHeightField(frame, key, parentHF.get(), hf, isFallback, SAMPLE_FIRST_VALID, interp, progress))
        {
            if ( isFallback && parentModel.valid() )
            {
                model->_normalData = parentModel->_normalData;
                model->_normalData._fallbackData = true;
            }
            else
            {
                model->_normalData = TileModel::NormalData(
                    hf,
                    GeoLocator::createForKey( key, mapInfo ),
                    isFallback );
            }
        }
    }

    else
    {
        // empty HF must be at least 2x2 for normal texture gen to work
        hf = HeightFieldUtils::createReferenceHeightField(
            key.getExtent(), EMPTY_NORMAL_MAP_SIZE, EMPTY_NORMAL_MAP_SIZE, true );

        model->_normalData = TileModel::NormalData(
            hf,
            GeoLocator::createForKey( key, mapInfo ),
            false );
    }

    if ( isFallback && parentModel.valid() )
    {
        model->_normalTexture = parentModel->_normalTexture.get();
    }
    else
    {
        model->generateNormalTexture();
    }
}
コード例 #23
0
ファイル: ElevationLayer.cpp プロジェクト: ldelgass/osgearth
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.
    LayerAndKeyVector contenders;
    LayerAndKeyVector offsets;

    // Track the number of layers that would return fallback data.
    unsigned numFallbackLayers = 0;

    // Check them in reverse order since the highest priority is last.
    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() );

            bool useLayer = true;
            TileKey bestKey( mappedKey );

            // Is there a tilesource? If not we are cache-only and cannot reject the layer.
            if ( layer->getTileSource() )
            {
                // Check whether the non-mapped key is valid according to the user's min/max level settings:
                if ( !layer->isKeyInRange(key) )
                {
                    useLayer = false;
                }
                

                // Find the "best available" mapped key from the tile source:
                else 
                {
                    if ( layer->getTileSource()->getBestAvailableTileKey(mappedKey, bestKey) )
                    {
                        // If the bestKey is not the mappedKey, this layer is providing
                        // fallback data (data at a lower resolution than requested)
                        if ( mappedKey != bestKey )
                        {
                            numFallbackLayers++;
                        }
                    }
                    else
                    {
                        useLayer = false;
                    }
                }
            }

            if ( useLayer )
            {
                if ( layer->isOffset() )
                {
                    offsets.push_back( std::make_pair(layer, bestKey) );
                }
                else
                {
                    contenders.push_back( std::make_pair(layer, bestKey) );
                }
            }
        }
    }

    // nothing? bail out.
    if ( contenders.empty() && offsets.empty() )
    {
        return false;
    }

    // if everything is fallback data, bail out.
    if ( contenders.size() + offsets.size() == numFallbackLayers )
    {
        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);

    // If the incoming heightfield requests a positive border width, 
    // we need to adjust the extents so that we request data outside the
    // extent of the tile key:
    unsigned border = hf->getBorderWidth();
    if (border > 0u)
    {
        dx = key.getExtent().width() / (double)(numColumns - (border*2+1));
        dy = key.getExtent().height() / (double)(numRows - (border*2+1));
        xmin -= dx * (double)border;
        ymin -= dy * (double)border;
    }
    
    // We will load the actual heightfields on demand. We might not need them all.
#if 0
    GeoHeightFieldVector heightFields(contenders.size());
    GeoHeightFieldVector offsetFields(offsets.size());
    std::vector<bool>    heightFallback(contenders.size(), false);
    std::vector<bool>    heightFailed(contenders.size(), false);
    std::vector<bool>    offsetFailed(offsets.size(), false);
#else
    GeoHeightFieldVector heightFields[9];
    GeoHeightFieldVector offsetFields[9]; //(offsets.size());
    std::vector<bool>    heightFallback[9]; //(contenders.size(), false);
    std::vector<bool>    heightFailed[9]; //(contenders.size(), false);
    std::vector<bool>    offsetFailed[9]; //(offsets.size(), false);

    for (int n = 0; n < 9; ++n)
    {
        heightFields[n].resize(contenders.size());
        offsetFields[n].resize(offsets.size());
        heightFallback[n].assign(9, false);
        heightFailed[n].assign(9, false);
        offsetFailed[n].assign(9, false);
    }
#endif

    // The maximum number of heightfields to keep in this local cache
    unsigned int maxHeightFields = 50;
    unsigned numHeightFieldsInCache = 0;

    const SpatialReference* keySRS = keyToUse.getProfile()->getSRS();

    bool realData = false;

    unsigned int total = numColumns * numRows;
    unsigned int completed = 0;
    int nodataCount = 0;

    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)
            {
                ElevationLayer* layer = contenders[i].first.get();                
                TileKey contenderKey = contenders[i].second;

                // If there is a border, the edge points may not fall within the key extents 
                // and we may need to fetch a neighboring key.

                int n = 4; // index 4 is the center/default tile

                if (border > 0u && !contenderKey.getExtent().contains(x, y))
                {
                    int dTx = x < contenderKey.getExtent().xMin() ? -1 : x > contenderKey.getExtent().xMax() ? +1 : 0;
                    int dTy = y < contenderKey.getExtent().yMin() ? +1 : y > contenderKey.getExtent().yMax() ? -1 : 0;
                    contenderKey = contenderKey.createNeighborKey(dTx, dTy);
                    n = (dTy+1)*3 + (dTx+1);
                }

                if ( heightFailed[n][i] )
                    continue;

                TileKey actualKey = contenderKey;

                GeoHeightField& layerHF = heightFields[n][i];

                if (!layerHF.valid())
                {
                    // We couldn't get the heightfield from the cache, so try to create it.
                    // We also fallback on parent layers to make sure that we have data at the location even if it's fallback.
                    while (!layerHF.valid() && actualKey.valid())
                    {
                        layerHF = layer->createHeightField(actualKey, progress);
                        if (!layerHF.valid())
                        {
                            actualKey = actualKey.createParentKey();
                        }
                    }

                    // Mark this layer as fallback if necessary.
                    if (layerHF.valid())
                    {
                        heightFallback[n][i] = (actualKey != contenderKey); // actualKey != contenders[i].second;
                        numHeightFieldsInCache++;
                    }
                    else
                    {
                        heightFailed[n][i] = true;
                        continue;
                    }
                }

                if (layerHF.valid())
                {
                    bool isFallback = heightFallback[n][i];

                    // We only have real data if this is not a fallback heightfield.
                    if (!isFallback)
                    {
                        realData = true;
                    }

                    float elevation;
                    if (layerHF.getElevation(keySRS, x, y, interpolation, keySRS, elevation))
                    {
                        if ( elevation != NO_DATA_VALUE )
                        {
                            resolved = true;                    
                            hf->setHeight(c, r, elevation);
                        }
                        else
                        {
                            ++nodataCount;
                        }
                    }
                }


                // Clear the heightfield cache if we have too many heightfields in the cache.
                if (numHeightFieldsInCache >= maxHeightFields)
                {
                    //OE_NOTICE << "Clearing cache" << std::endl;
                    for (unsigned int j = 0; j < 9; ++j)
                    {
                        for (unsigned int k = 0; k < heightFields[j].size(); k++)
                        {
                            heightFields[j][k] = GeoHeightField::INVALID;
                            heightFallback[j][k] = false;
                        }
                    }
                    numHeightFieldsInCache = 0;
                }
            }

            for(int i=offsets.size()-1; i>=0; --i)
            {
                TileKey contenderKey = offsets[i].second;

                // If there is a border, the edge points may not fall within the key extents 
                // and we may need to fetch a neighboring key.

                int n = 4; // index 4 is the center/default tile

                if (border > 0u && !contenderKey.getExtent().contains(x, y))
                {
                    int dTx = x < contenderKey.getExtent().xMin() ? -1 : x > contenderKey.getExtent().xMax() ? +1 : 0;
                    int dTy = y < contenderKey.getExtent().yMin() ? +1 : x > contenderKey.getExtent().yMax() ? -1 : 0;
                    contenderKey = contenderKey.createNeighborKey(dTx, dTy);
                    n = (dTy+1)*3 + (dTx+1);
                }
                
                if ( offsetFailed[n][i] == true )
                    continue;

                GeoHeightField& layerHF = offsetFields[n][i];
                if ( !layerHF.valid() )
                {
                    ElevationLayer* offset = offsets[i].first.get();

                    layerHF = offset->createHeightField(contenderKey, progress);
                    if ( !layerHF.valid() )
                    {
                        offsetFailed[n][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;
}
コード例 #24
0
osg::Image*
CompositeTileSource::createImage(const TileKey&    key,
                                 ProgressCallback* progress )
{
    ImageMixVector images;
    images.reserve( _options._components.size() );

    for(CompositeTileSourceOptions::ComponentVector::const_iterator i = _options._components.begin();
        i != _options._components.end();
        ++i )
    {
        if ( progress && progress->isCanceled() )
            return 0L;

        ImageInfo imageInfo;
        imageInfo.dataInExtents = false;



        TileSource* source = i->_tileSourceInstance.get();
        if ( source )
        {
            //TODO:  This duplicates code in ImageLayer::isKeyValid.  Maybe should move that to TileSource::isKeyValid instead
            int minLevel = 0;
            int maxLevel = INT_MAX;
            if (i->_imageLayerOptions->minLevel().isSet())
            {
                minLevel = i->_imageLayerOptions->minLevel().value();
            }
            else if (i->_imageLayerOptions->minResolution().isSet())
            {
                minLevel = source->getProfile()->getLevelOfDetailForHorizResolution( 
                    i->_imageLayerOptions->minResolution().value(), 
                    source->getPixelsPerTile());
            }

            if (i->_imageLayerOptions->maxLevel().isSet())
            {
                maxLevel = i->_imageLayerOptions->maxLevel().value();
            }
            else if (i->_imageLayerOptions->maxResolution().isSet())
            {
                maxLevel = source->getProfile()->getLevelOfDetailForHorizResolution( 
                    i->_imageLayerOptions->maxResolution().value(), 
                    source->getPixelsPerTile());
            }

            // check that this source is within the level bounds:
            if (minLevel > (int)key.getLevelOfDetail() ||
                maxLevel < (int)key.getLevelOfDetail() )
            {
                continue;
            }
            
                //Only try to get data if the source actually has data                
                if (source->hasDataInExtent( key.getExtent() ) )
                {
                    //We have data within these extents
                    imageInfo.dataInExtents = true;

                    if ( !source->getBlacklist()->contains( key.getTileId() ) )
                    {                        
                        osg::ref_ptr< ImageLayerPreCacheOperation > preCacheOp;
                        if ( i->_imageLayerOptions.isSet() )
                        {
                            preCacheOp = new ImageLayerPreCacheOperation();
                            preCacheOp->_processor.init( i->_imageLayerOptions.value(), _dbOptions.get(), true );                        
                        }

                        imageInfo.image = source->createImage( key, preCacheOp.get(), progress );
                        imageInfo.opacity = 1.0f;

                        //If the image is not valid and the progress was not cancelled, blacklist
                        if (!imageInfo.image.valid() && (!progress || !progress->isCanceled()))
                        {
                            //Add the tile to the blacklist
                            OE_DEBUG << LC << "Adding tile " << key.str() << " to the blacklist" << std::endl;
                            source->getBlacklist()->add( key.getTileId() );
                        }
                        imageInfo.opacity = i->_imageLayerOptions.isSet() ? i->_imageLayerOptions->opacity().value() : 1.0f;
                    }
                }
                else
                {
                    OE_DEBUG << LC << "Source has no data at " << key.str() << std::endl;
                }
        }

        //Add the ImageInfo to the list
        images.push_back( imageInfo );
    }

    unsigned numValidImages = 0;
    osg::Vec2s textureSize;
    for (unsigned int i = 0; i < images.size(); i++)
    {
        ImageInfo& info = images[i];
        if (info.image.valid())
        {
            if (numValidImages == 0)
            {
                textureSize.set( info.image->s(), info.image->t());
            }
            numValidImages++;        
        }
    }

    //Try to fallback on any empty images if we have some valid images but not valid images for ALL layers
    if (numValidImages > 0 && numValidImages < images.size())
    {        
        for (unsigned int i = 0; i < images.size(); i++)
        {
            ImageInfo& info = images[i];
            if (!info.image.valid() && info.dataInExtents)
            {                                
                TileKey parentKey = key.createParentKey();

                TileSource* source = _options._components[i]._tileSourceInstance;
                if (source)
                {                 
                    osg::ref_ptr< ImageLayerPreCacheOperation > preCacheOp;
                    if ( _options._components[i]._imageLayerOptions.isSet() )
                    {
                        preCacheOp = new ImageLayerPreCacheOperation();
                        preCacheOp->_processor.init( _options._components[i]._imageLayerOptions.value(), _dbOptions.get(), true );                        
                    }                

                    osg::ref_ptr< osg::Image > image;
                    while (!image.valid() && parentKey.valid())
                    {                        
                        image = source->createImage( parentKey, preCacheOp.get(), progress );
                        if (image.valid())
                        {                     
                            break;
                        }
                        parentKey = parentKey.createParentKey();
                    }     

                    if (image.valid())
                    {
                        //We got an image, but now we need to crop it to match the incoming key's extents
                        GeoImage geoImage( image.get(), parentKey.getExtent());
                        GeoImage cropped = geoImage.crop( key.getExtent(), true, textureSize.x(), textureSize.y(), *source->_options.bilinearReprojection());
                        image = cropped.getImage();
                    }

                    info.image = image.get();
                }
            }
        }
    }

    //Recompute the number of valid images
    numValidImages = 0;
    for (unsigned int i = 0; i < images.size(); i++)
    {
        ImageInfo& info = images[i];
        if (info.image.valid()) numValidImages++;        
    }

    if ( progress && progress->isCanceled() )
    {
        return 0L;
    }
    else if ( numValidImages == 0 )
    {
        return 0L;
    }
    else if ( numValidImages == 1 )
    {
        //We only have one valid image, so just return it and don't bother with compositing
        for (unsigned int i = 0; i < images.size(); i++)
        {
            ImageInfo& info = images[i];
            if (info.image.valid())
                return info.image.release();
        }
        return 0L;
    }
    else
    {
        osg::Image* result = 0;
        for (unsigned int i = 0; i < images.size(); i++)
        {
            ImageInfo& imageInfo = images[i];
            if (!result)
            {
                if (imageInfo.image.valid())
                {
                    result = new osg::Image( *imageInfo.image.get());
                }
            }
            else
            {
                if (imageInfo.image.valid())
                {
                    ImageUtils::mix( result, imageInfo.image, imageInfo.opacity );
                }
            }            
        }        
        return result;
    }
}
コード例 #25
0
ファイル: TileModelFactory.cpp プロジェクト: pka/osgearth
void
TileModelFactory::createTileModel(const TileKey&           key, 
                                  osg::ref_ptr<TileModel>& out_model,
                                  bool&                    out_hasRealData)
{
    MapFrame mapf( _map, Map::MASKED_TERRAIN_LAYERS );
    
    const MapInfo& mapInfo = mapf.getMapInfo();

    osg::ref_ptr<TileModel> model = new TileModel();
    model->_map         = _map;
    model->_tileKey     = key;
    model->_tileLocator = GeoLocator::createForKey(key, mapInfo);

    // init this to false, then search for real data. "Real data" is data corresponding
    // directly to the key, as opposed to fallback data, which is derived from a lower
    // LOD key.
    out_hasRealData = false;
    
    // Fetch the image data and make color layers.
    unsigned order = 0;
    for( ImageLayerVector::const_iterator i = mapf.imageLayers().begin(); i != mapf.imageLayers().end(); ++i )
    {
        ImageLayer* layer = i->get();

        if ( layer->getEnabled() )
        {
            BuildColorData build;
            build.init( key, layer, order, mapInfo, _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, mapf, _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.
    if ( !model->_elevationData.getHeightField() )
    {
        osg::HeightField* hf = HeightFieldUtils::createReferenceHeightField( key.getExtent(), 8, 8 );
        model->_elevationData = TileModel::ElevationData(
            hf,
            GeoLocator::createForKey(key, mapInfo),
            true );
    }

    if (!out_hasRealData)
    {
        // Check the results and see if we have any real data.
        for( TileModel::ColorDataByUID::const_iterator i = model->_colorData.begin(); i != model->_colorData.end(); ++i )
        {
            if ( !i->second.isFallbackData() ) 
            {
                out_hasRealData = true;
                break;
            }
        }
    }

    if ( !out_hasRealData && !model->_elevationData.isFallbackData() )
    {
        out_hasRealData = 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();
}
コード例 #26
0
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();
}
コード例 #27
0
ファイル: ElevationQuery.cpp プロジェクト: Vantica/osgearth
bool
ElevationQuery::getElevationImpl(const GeoPoint& point, /* abs */
                                 double&         out_elevation,
                                 double          desiredResolution,
                                 double*         out_actualResolution)
{
    // assertion.
    if ( !point.isAbsolute() )
    {
        OE_WARN << LC << "Assertion failure; input must be absolute" << std::endl;
        return false;
    }

    osg::Timer_t begin = osg::Timer::instance()->tick();

    // first try the terrain patches.
    if ( _patchLayers.size() > 0 )
    {
        osgUtil::IntersectionVisitor iv;

        for(std::vector<ModelLayer*>::iterator i = _patchLayers.begin(); i != _patchLayers.end(); ++i)
        {
            // find the scene graph for this layer:
            osg::Node* node = (*i)->getSceneGraph( _mapf.getUID() );
            if ( node )
            {
                // configure for intersection:
                osg::Vec3d surface;
                point.toWorld( surface );

                // trivial bounds check:
                if ( node->getBound().contains(surface) )
                {
                    osg::Vec3d nvector;
                    point.createWorldUpVector(nvector);

                    osg::Vec3d start( surface + nvector*5e5 );
                    osg::Vec3d end  ( surface - nvector*5e5 );
                
                    // first time through, set up the intersector on demand
                    if ( !_patchLayersLSI.valid() )
                    {
                        _patchLayersLSI = new DPLineSegmentIntersector(start, end);
                        _patchLayersLSI->setIntersectionLimit( _patchLayersLSI->LIMIT_NEAREST );
                    }
                    else
                    {
                        _patchLayersLSI->reset();
                        _patchLayersLSI->setStart( start );
                        _patchLayersLSI->setEnd  ( end );
                    }

                    // try it.
                    iv.setIntersector( _patchLayersLSI.get() );
                    node->accept( iv );

                    // check for a result!!
                    if ( _patchLayersLSI->containsIntersections() )
                    {
                        osg::Vec3d isect = _patchLayersLSI->getIntersections().begin()->getWorldIntersectPoint();

                        // transform back to input SRS:
                        GeoPoint output;
                        output.fromWorld( point.getSRS(), isect );
                        out_elevation = output.z();
                        if ( out_actualResolution )
                            *out_actualResolution = 0.0;

                        return true;
                    }
                }
                else
                {
                    //OE_INFO << LC << "Trivial rejection (bounds check)" << std::endl;
                }
            }
        }
    }

    if ( _mapf.elevationLayers().empty() )
    {
        // this means there are no heightfields.
        out_elevation = 0.0;
        return true;        
    }

    // tile size (resolution of elevation tiles)
    unsigned tileSize = std::max(_mapf.getMapOptions().elevationTileSize().get(), 2u);

    //This is the max resolution that we actually have data at this point
    unsigned int bestAvailLevel = getMaxLevel( point.x(), point.y(), point.getSRS(), _mapf.getProfile());

    if (desiredResolution > 0.0)
    {
        unsigned int desiredLevel = _mapf.getProfile()->getLevelOfDetailForHorizResolution( desiredResolution, tileSize );
        if (desiredLevel < bestAvailLevel) bestAvailLevel = desiredLevel;
    }

    OE_DEBUG << LC << "Best available data level " << point.x() << ", " << point.y() << " = "  << bestAvailLevel << std::endl;

    // transform the input coords to map coords:
    GeoPoint mapPoint = point;
    if ( point.isValid() && !point.getSRS()->isHorizEquivalentTo( _mapf.getProfile()->getSRS() ) )
    {
        mapPoint = point.transform(_mapf.getProfile()->getSRS());
        if ( !mapPoint.isValid() )
        {
            OE_WARN << LC << "Fail: coord transform failed" << std::endl;
            return false;
        }
    }    

    // get the tilekey corresponding to the tile we need:
    TileKey key = _mapf.getProfile()->createTileKey( mapPoint.x(), mapPoint.y(), bestAvailLevel );
    if ( !key.valid() )
    {
        OE_WARN << LC << "Fail: coords fall outside map" << std::endl;
        return false;
    }
        
    bool result = false;      
    while (!result)
    {      
        GeoHeightField geoHF;
        TileCache::Record record;
        // Try to get the hf from the cache
        if ( _cache.get( key, record ) )
        {                        
            geoHF = record.value();
        }
        else
        {
            // Create it            
            osg::ref_ptr<osg::HeightField> hf = new osg::HeightField();
            hf->allocate( tileSize, tileSize );

            // Initialize the heightfield to nodata
            for (unsigned int i = 0; i < hf->getFloatArray()->size(); i++)
            {
                hf->getFloatArray()->at( i ) = NO_DATA_VALUE;
            }   

            if (_mapf.populateHeightField(hf, key, false))
            {                
                geoHF = GeoHeightField( hf.get(), key.getExtent() );
                _cache.insert( key, geoHF );
            }
        }

        if (geoHF.valid())
        {            
            float elevation = 0.0f;                 
            result = geoHF.getElevation( mapPoint.getSRS(), mapPoint.x(), mapPoint.y(), _mapf.getMapInfo().getElevationInterpolation(), mapPoint.getSRS(), elevation);                              
            if (result && elevation != NO_DATA_VALUE)
            {                        
                // see what the actual resolution of the heightfield is.
                if ( out_actualResolution )
                    *out_actualResolution = geoHF.getXInterval(); 
                out_elevation = (double)elevation;                
                break;
            }
            else
            {                               
                result = false;
            }
        }

        if (!result)
        {
            key = key.createParentKey();                        
            if (!key.valid())
            {
                break;
            }
        }         
    }

         

    osg::Timer_t end = osg::Timer::instance()->tick();
    _queries++;
    _totalTime += osg::Timer::instance()->delta_s( begin, end );

    return result;
}