osgEarth 2.1.1
Static Public Member Functions

osgEarth::HeightFieldUtils Class Reference

List of all members.

Static Public Member Functions

static float getHeightAtPixel (const osg::HeightField *hf, double c, double r, ElevationInterpolation interpoltion=INTERP_BILINEAR)
static float getHeightAtLocation (const osg::HeightField *hf, double x, double y, double llx, double lly, double dx, double dy, ElevationInterpolation interpolation=INTERP_BILINEAR)
static float getHeightAtNormalizedLocation (const osg::HeightField *hf, double nx, double ny, ElevationInterpolation interp=INTERP_BILINEAR)
static void scaleHeightFieldToDegrees (osg::HeightField *hf)
static osg::HeightField * createSubSample (osg::HeightField *input, const class GeoExtent &inputEx, const class GeoExtent &outputEx, ElevationInterpolation interpolation=INTERP_BILINEAR)
static osg::HeightField * resizeHeightField (osg::HeightField *input, int newX, int newY, ElevationInterpolation interp=INTERP_BILINEAR)
static
osg::ClusterCullingCallback * 
createClusterCullingCallback (osg::HeightField *grid, osg::EllipsoidModel *em, float verticalScale=1.0f)

Detailed Description

Definition at line 54 of file HeightFieldUtils.


Member Function Documentation

osg::ClusterCullingCallback * HeightFieldUtils::createClusterCullingCallback ( osg::HeightField *  grid,
osg::EllipsoidModel *  em,
float  verticalScale = 1.0f 
) [static]

Creates a new cluster culler based on a heightfield. Cluster cullers are for geocentric maps only, and therefore requires the ellipsoid model.

Definition at line 308 of file HeightFieldUtils.cpp.

{
    //This code is a very slightly modified version of the DestinationTile::createClusterCullingCallback in VirtualPlanetBuilder.
    if ( !grid || !et )
        return 0L;

    double globe_radius = et->getRadiusPolar();
    unsigned int numColumns = grid->getNumColumns();
    unsigned int numRows = grid->getNumRows();

    double midLong = grid->getOrigin().x()+grid->getXInterval()*((double)(numColumns-1))*0.5;
    double midLat = grid->getOrigin().y()+grid->getYInterval()*((double)(numRows-1))*0.5;
    double midZ = grid->getOrigin().z();

    double midX,midY;
    et->convertLatLongHeightToXYZ(osg::DegreesToRadians(midLat),osg::DegreesToRadians(midLong),midZ, midX,midY,midZ);

    osg::Vec3 center_position(midX,midY,midZ);
    osg::Vec3 center_normal(midX,midY,midZ);
    center_normal.normalize();

    osg::Vec3 transformed_center_normal = center_normal;

    unsigned int r,c;

    // populate the vertex/normal/texcoord arrays from the grid.
    double orig_X = grid->getOrigin().x();
    double delta_X = grid->getXInterval();
    double orig_Y = grid->getOrigin().y();
    double delta_Y = grid->getYInterval();
    double orig_Z = grid->getOrigin().z();


    float min_dot_product = 1.0f;
    float max_cluster_culling_height = 0.0f;
    float max_cluster_culling_radius = 0.0f;

    for(r=0;r<numRows;++r)
    {
        for(c=0;c<numColumns;++c)
        {
            double X = orig_X + delta_X*(double)c;
            double Y = orig_Y + delta_Y*(double)r;
            double Z = orig_Z + grid->getHeight(c,r) * verticalScale;
            double height = Z;

            et->convertLatLongHeightToXYZ(
                osg::DegreesToRadians(Y), osg::DegreesToRadians(X), Z,
                X, Y, Z);

            osg::Vec3d v(X,Y,Z);
            osg::Vec3 dv = v - center_position;
            double d = sqrt(dv.x()*dv.x() + dv.y()*dv.y() + dv.z()*dv.z());
            double theta = acos( globe_radius/ (globe_radius + fabs(height)) );
            double phi = 2.0 * asin (d*0.5/globe_radius); // d/globe_radius;
            double beta = theta+phi;
            double cutoff = osg::PI_2 - 0.1;

            //log(osg::INFO,"theta="<<theta<<"\tphi="<<phi<<" beta "<<beta);
            if (phi<cutoff && beta<cutoff)
            {
                float local_dot_product = -sin(theta + phi);
                float local_m = globe_radius*( 1.0/ cos(theta+phi) - 1.0);
                float local_radius = static_cast<float>(globe_radius * tan(beta)); // beta*globe_radius;
                min_dot_product = osg::minimum(min_dot_product, local_dot_product);
                max_cluster_culling_height = osg::maximum(max_cluster_culling_height,local_m);      
                max_cluster_culling_radius = osg::maximum(max_cluster_culling_radius,local_radius);
            }
            else
            {
                //log(osg::INFO,"Turning off cluster culling for wrap around tile.");
                return 0;
            }
        }
    }    

    osg::ClusterCullingCallback* ccc = new osg::ClusterCullingCallback;

    ccc->set(center_position + transformed_center_normal*max_cluster_culling_height ,
        transformed_center_normal, 
        min_dot_product,
        max_cluster_culling_radius);

    return ccc;
}
osg::HeightField * HeightFieldUtils::createSubSample ( osg::HeightField *  input,
const class GeoExtent inputEx,
const class GeoExtent outputEx,
ElevationInterpolation  interpolation = INTERP_BILINEAR 
) [static]

Subsamples a heightfield to the specified extent.

Definition at line 225 of file HeightFieldUtils.cpp.

{
    double div = outputEx.width()/inputEx.width();
    if ( div >= 1.0f )
        return 0L;

    int numCols = input->getNumColumns();
    int numRows = input->getNumRows();

    //float dx = input->getXInterval() * div;
    //float dy = input->getYInterval() * div;

    double xInterval = inputEx.width()  / (double)(input->getNumColumns()-1);
    double yInterval = inputEx.height()  / (double)(input->getNumRows()-1);
    double dx = div * xInterval;
    double dy = div * yInterval;


    osg::HeightField* dest = new osg::HeightField();
    dest->allocate( numCols, numRows );
    dest->setXInterval( dx );
    dest->setYInterval( dy );

    // copy over the skirt height, adjusting it for relative tile size.
    dest->setSkirtHeight( input->getSkirtHeight() * div );

    double x, y;
    int col, row;

    for( x = outputEx.xMin(), col=0; col < numCols; x += dx, col++ )
    {
        for( y = outputEx.yMin(), row=0; row < numRows; y += dy, row++ )
        {
            float height = HeightFieldUtils::getHeightAtLocation( input, x, y, inputEx.xMin(), inputEx.yMin(), xInterval, yInterval, interpolation);
            dest->setHeight( col, row, height );
        }
    }

    osg::Vec3d orig( outputEx.xMin(), outputEx.yMin(), input->getOrigin().z() );
    dest->setOrigin( orig );

    return dest;
}

Here is the call graph for this function:

float HeightFieldUtils::getHeightAtLocation ( const osg::HeightField *  hf,
double  x,
double  y,
double  llx,
double  lly,
double  dx,
double  dy,
ElevationInterpolation  interpolation = INTERP_BILINEAR 
) [static]

Gets the interpolated height value at the specific geolocation.

Definition at line 184 of file HeightFieldUtils.cpp.

{
    //Determine the pixel to sample
    double px = osg::clampBetween( (x - llx) / dx, 0.0, (double)(hf->getNumColumns()-1) );
    double py = osg::clampBetween( (y - lly) / dy, 0.0, (double)(hf->getNumRows()-1) );
    return getHeightAtPixel(hf, px, py, interpolation);
}

Here is the call graph for this function:

Here is the caller graph for this function:

float HeightFieldUtils::getHeightAtNormalizedLocation ( const osg::HeightField *  hf,
double  nx,
double  ny,
ElevationInterpolation  interp = INTERP_BILINEAR 
) [static]

Gets the interpolated elevation at the specified "normalized unit location". i.e., nx => [0.0, 1.0], ny => [0.0, 1.0] where 0.0 and 1.0 are the opposing endposts of the heightfield.

Definition at line 193 of file HeightFieldUtils.cpp.

{
    double px = nx * (double)(input->getNumColumns() - 1);
    double py = ny * (double)(input->getNumRows() - 1);
    return getHeightAtPixel( input, px, py, interp );
}

Here is the call graph for this function:

Here is the caller graph for this function:

float HeightFieldUtils::getHeightAtPixel ( const osg::HeightField *  hf,
double  c,
double  r,
ElevationInterpolation  interpoltion = INTERP_BILINEAR 
) [static]

Gets the interpolated height value at the specified fractional pixel position.

Definition at line 27 of file HeightFieldUtils.cpp.

{
    float result = 0.0;
    if (interpolation == INTERP_NEAREST)
    {
        //Nearest interpolation
        result = hf->getHeight((unsigned int)osg::round(c), (unsigned int)osg::round(r));
    }
    else if (interpolation == INTERP_TRIANGULATE)
    {
        //Interpolation to make sure that the interpolated point follows the triangles generated by the 4 parent points
        int rowMin = osg::maximum((int)floor(r), 0);
        int rowMax = osg::maximum(osg::minimum((int)ceil(r), (int)(hf->getNumRows()-1)), 0);
        int colMin = osg::maximum((int)floor(c), 0);
        int colMax = osg::maximum(osg::minimum((int)ceil(c), (int)(hf->getNumColumns()-1)), 0);

        if (rowMin == rowMax)
        {
            if (rowMin < (int)hf->getNumRows()-2)
            {
                rowMax = rowMin + 1;
            }
            else
            {
                rowMin = rowMax - 1;
            }
         }

         if (colMin == colMax)
         {
            if (colMin < (int)hf->getNumColumns()-2)
            {
                colMax = colMin + 1;
            }
            else
            {
               colMin = colMax - 1;
            }
         }

        if (rowMin > rowMax) rowMin = rowMax;
        if (colMin > colMax) colMin = colMax;

        float urHeight = hf->getHeight(colMax, rowMax);
        float llHeight = hf->getHeight(colMin, rowMin);
        float ulHeight = hf->getHeight(colMin, rowMax);
        float lrHeight = hf->getHeight(colMax, rowMin);

        //Make sure not to use NoData in the interpolation
        if (urHeight == NO_DATA_VALUE || llHeight == NO_DATA_VALUE || ulHeight == NO_DATA_VALUE || lrHeight == NO_DATA_VALUE)
        {
            return NO_DATA_VALUE;
        }

        double dx = c - (double)colMin;
        double dy = r - (double)rowMin;

        //The quad consisting of the 4 corner points can be made into two triangles.
        //The "left" triangle is ll, ur, ul
        //The "right" triangle is ll, lr, ur

        //Determine which triangle the point falls in.
        osg::Vec3d v0, v1, v2;
        if (dx > dy)
        {
            //The point lies in the right triangle
            v0.set(colMin, rowMin, llHeight);
            v1.set(colMax, rowMin, lrHeight);
            v2.set(colMax, rowMax, urHeight);
        }
        else
        {
            //The point lies in the left triangle
            v0.set(colMin, rowMin, llHeight);
            v1.set(colMax, rowMax, urHeight);
            v2.set(colMin, rowMax, ulHeight);
        }

        //Compute the normal
        osg::Vec3d n = (v1 - v0) ^ (v2 - v0);

        result = ( n.x() * ( c - v0.x() ) + n.y() * ( r - v0.y() ) ) / -n.z() + v0.z();
    }
    else
    {
        //OE_INFO << "getHeightAtPixel: (" << c << ", " << r << ")" << std::endl;
        int rowMin = osg::maximum((int)floor(r), 0);
        int rowMax = osg::maximum(osg::minimum((int)ceil(r), (int)(hf->getNumRows()-1)), 0);
        int colMin = osg::maximum((int)floor(c), 0);
        int colMax = osg::maximum(osg::minimum((int)ceil(c), (int)(hf->getNumColumns()-1)), 0);

        if (rowMin > rowMax) rowMin = rowMax;
        if (colMin > colMax) colMin = colMax;

        float urHeight = hf->getHeight(colMax, rowMax);
        float llHeight = hf->getHeight(colMin, rowMin);
        float ulHeight = hf->getHeight(colMin, rowMax);
        float lrHeight = hf->getHeight(colMax, rowMin);

        //Make sure not to use NoData in the interpolation
        if (urHeight == NO_DATA_VALUE || llHeight == NO_DATA_VALUE || ulHeight == NO_DATA_VALUE || lrHeight == NO_DATA_VALUE)
        {
            return NO_DATA_VALUE;
        }

        //OE_INFO << "Heights (ll, lr, ul, ur) ( " << llHeight << ", " << urHeight << ", " << ulHeight << ", " << urHeight << std::endl;

        if (interpolation == INTERP_BILINEAR)
        {
            //Check for exact value
            if ((colMax == colMin) && (rowMax == rowMin))
            {
                //OE_NOTICE << "Exact value" << std::endl;
                result = hf->getHeight((int)c, (int)r);
            }
            else if (colMax == colMin)
            {
                //OE_NOTICE << "Vertically" << std::endl;
                //Linear interpolate vertically
                result = ((double)rowMax - r) * llHeight + (r - (double)rowMin) * ulHeight;
            }
            else if (rowMax == rowMin)
            {
                //OE_NOTICE << "Horizontally" << std::endl;
                //Linear interpolate horizontally
                result = ((double)colMax - c) * llHeight + (c - (double)colMin) * lrHeight;
            }
            else
            {
                //OE_NOTICE << "Bilinear" << std::endl;
                //Bilinear interpolate
                float r1 = ((double)colMax - c) * llHeight + (c - (double)colMin) * lrHeight;
                float r2 = ((double)colMax - c) * ulHeight + (c - (double)colMin) * urHeight;

                //OE_INFO << "r1, r2 = " << r1 << " , " << r2 << std::endl;

                result = ((double)rowMax - r) * r1 + (r - (double)rowMin) * r2;
            }
        }
        else if (interpolation == INTERP_AVERAGE)
        {
            double x_rem = c - (int)c;
            double y_rem = r - (int)r;

            double w00 = (1.0 - y_rem) * (1.0 - x_rem) * (double)llHeight;
            double w01 = (1.0 - y_rem) * x_rem * (double)lrHeight;
            double w10 = y_rem * (1.0 - x_rem) * (double)ulHeight;
            double w11 = y_rem * x_rem * (double)urHeight;

            result = (float)(w00 + w01 + w10 + w11);
        }
    }

    return result;
}

Here is the caller graph for this function:

osg::HeightField * HeightFieldUtils::resizeHeightField ( osg::HeightField *  input,
int  newX,
int  newY,
ElevationInterpolation  interp = INTERP_BILINEAR 
) [static]

Resizes a heightfield, keeping the corner values the same and resampling the internal posts.

Definition at line 271 of file HeightFieldUtils.cpp.

{
    if ( newColumns <= 1 && newRows <= 1 )
        return 0L;

    if ( newColumns == input->getNumColumns() && newRows == (int)input->getNumRows() )
        return new osg::HeightField( *input, osg::CopyOp::DEEP_COPY_ALL );

    double spanX = (input->getNumColumns()-1) * input->getXInterval();
    double spanY = (input->getNumRows()-1) * input->getYInterval();
    const osg::Vec3& origin = input->getOrigin();

    double stepX = spanX/(double)(newColumns-1);
    double stepY = spanY/(double)(newRows-1);

    osg::HeightField* output = new osg::HeightField();
    output->allocate( newColumns, newRows );
    output->setXInterval( stepX );
    output->setYInterval( stepY );
    output->setOrigin( origin );
    
    for( int y = 0; y < newRows; ++y )
    {
        for( int x = 0; x < newColumns; ++x )
        {
            double nx = (double)x / (double)(newColumns-1);
            double ny = (double)y / (double)(newRows-1);
            float h = getHeightAtNormalizedLocation( input, nx, ny );
            output->setHeight( x, y, h );
        }
    }

    return output;
}

Here is the call graph for this function:

void HeightFieldUtils::scaleHeightFieldToDegrees ( osg::HeightField *  hf) [static]

Scales all the height values in a heightfield from scalar units to "linear degrees". The only purpose of this is to show reasonable height values in a projected Plate Carre map (in which vertical units are not well defined).

Definition at line 204 of file HeightFieldUtils.cpp.

{
    if (hf)
    {
        //The number of degrees in a meter at the equator
        //TODO: adjust this calculation based on the actual EllipsoidModel.
        float scale = 1.0f/111319.0f;

        for (unsigned int i = 0; i < hf->getHeightList().size(); ++i)
        {
            hf->getHeightList()[i] *= scale;
        }
    }
    else
    {
        OE_WARN << "[osgEarth::HeightFieldUtils] scaleHeightFieldToDegrees heightfield is NULL" << std::endl;
    }
}

Here is the caller graph for this function:


The documentation for this class was generated from the following files:
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines