Beispiel #1
0
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;
}
Beispiel #2
0
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;

    GeoHeightFieldVector offsetHeightFields;

    //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.

    for( ElevationLayerVector::const_iterator i = this->begin(); i != this->end(); i++ )
    {
        ElevationLayer* layer = i->get();

        if ( layer->getEnabled() && layer->getVisible() )
        {
            GeoHeightField geoHF;
            if ( layer->isKeyValid(keyToUse) )
            {
                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() )
            {
                //If the layer is offset, add it to the list of offset heightfields
                if (*layer->getElevationLayerOptions().offset())
                {                    
                    offsetHeightFields.push_back( geoHF );
                }
                //Otherwise add it to the list of regular heightfields
                else
                {
                    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 )
        {
            unsigned defaultSize = _expressTileSize.getOrUse( 7 );

            out_result = HeightFieldUtils::createReferenceHeightField( 
                keyToUse.getExtent(), 
                defaultSize, 
                defaultSize );

            if ( offsetHeightFields.size() == 0 )
            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;
        }

        // resample if necessary:
        if ( _expressTileSize.isSet() )
        {
            out_result = HeightFieldUtils::resampleHeightField(
                out_result.get(),
                key.getExtent(),
                *_expressTileSize,
                *_expressTileSize,
                interpolation );
        }
    }

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

        if ( _expressTileSize.isSet() )
        {
            // user set a tile size; use it.
            width  = *_expressTileSize;
            height = *_expressTileSize;
        }
        else
        {
            // user did not ask for a tile size; find the biggest among the layers.
            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();
            }
        }

        // make the new heightfield.
        out_result = new osg::HeightField();
        out_result->allocate( width, height );

        // calculate the post spacings.
        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 layer heightfields.
        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 );
    }

    // Initialize the HF values
    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 );
    }

    // Add any "offset" elevation layers to the resulting heightfield
    if (out_result.valid() && offsetHeightFields.size() )
    {        
        // calculate the post spacings.
        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();

        for( GeoHeightFieldVector::iterator itr = offsetHeightFields.begin(); itr != offsetHeightFields.end(); ++itr )
        {
            for (unsigned int c = 0; c < out_result->getNumColumns(); c++)
            {
                double x = minx + (dx * (double)c);
                for (unsigned int r = 0; r < out_result->getNumRows(); r++)
                {                         
                    double y = miny + (dy * (double)r);
                    float elevation = 0.0;                    
                    if (itr->getElevation(keySRS, x, y, interpolation, keySRS, elevation))
                    {                    
                        double h = out_result->getHeight( c, r );                        
                        h += elevation;                                     
                        out_result->setHeight( c, r, h );
                    }                                
                }
            }
        }
    }

    return out_result.valid();
}