osgEarth 2.1.1

/home/cube/sources/osgearth/src/osgEarth/HeightFieldUtils.cpp

Go to the documentation of this file.
00001 /* -*-c++-*- */
00002 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
00003  * Copyright 2008-2010 Pelican Mapping
00004  * http://osgearth.org
00005  *
00006  * osgEarth is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU Lesser General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU Lesser General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU Lesser General Public License
00017  * along with this program.  If not, see <http://www.gnu.org/licenses/>
00018  */
00019 
00020 #include <osgEarth/HeightFieldUtils>
00021 #include <osgEarth/GeoData>
00022 #include <osg/Notify>
00023 
00024 using namespace osgEarth;
00025 
00026 float
00027 HeightFieldUtils::getHeightAtPixel(const osg::HeightField* hf, double c, double r, ElevationInterpolation interpolation)
00028 {
00029     float result = 0.0;
00030     if (interpolation == INTERP_NEAREST)
00031     {
00032         //Nearest interpolation
00033         result = hf->getHeight((unsigned int)osg::round(c), (unsigned int)osg::round(r));
00034     }
00035     else if (interpolation == INTERP_TRIANGULATE)
00036     {
00037         //Interpolation to make sure that the interpolated point follows the triangles generated by the 4 parent points
00038         int rowMin = osg::maximum((int)floor(r), 0);
00039         int rowMax = osg::maximum(osg::minimum((int)ceil(r), (int)(hf->getNumRows()-1)), 0);
00040         int colMin = osg::maximum((int)floor(c), 0);
00041         int colMax = osg::maximum(osg::minimum((int)ceil(c), (int)(hf->getNumColumns()-1)), 0);
00042 
00043         if (rowMin == rowMax)
00044         {
00045             if (rowMin < (int)hf->getNumRows()-2)
00046             {
00047                 rowMax = rowMin + 1;
00048             }
00049             else
00050             {
00051                 rowMin = rowMax - 1;
00052             }
00053          }
00054 
00055          if (colMin == colMax)
00056          {
00057             if (colMin < (int)hf->getNumColumns()-2)
00058             {
00059                 colMax = colMin + 1;
00060             }
00061             else
00062             {
00063                colMin = colMax - 1;
00064             }
00065          }
00066 
00067         if (rowMin > rowMax) rowMin = rowMax;
00068         if (colMin > colMax) colMin = colMax;
00069 
00070         float urHeight = hf->getHeight(colMax, rowMax);
00071         float llHeight = hf->getHeight(colMin, rowMin);
00072         float ulHeight = hf->getHeight(colMin, rowMax);
00073         float lrHeight = hf->getHeight(colMax, rowMin);
00074 
00075         //Make sure not to use NoData in the interpolation
00076         if (urHeight == NO_DATA_VALUE || llHeight == NO_DATA_VALUE || ulHeight == NO_DATA_VALUE || lrHeight == NO_DATA_VALUE)
00077         {
00078             return NO_DATA_VALUE;
00079         }
00080 
00081         double dx = c - (double)colMin;
00082         double dy = r - (double)rowMin;
00083 
00084         //The quad consisting of the 4 corner points can be made into two triangles.
00085         //The "left" triangle is ll, ur, ul
00086         //The "right" triangle is ll, lr, ur
00087 
00088         //Determine which triangle the point falls in.
00089         osg::Vec3d v0, v1, v2;
00090         if (dx > dy)
00091         {
00092             //The point lies in the right triangle
00093             v0.set(colMin, rowMin, llHeight);
00094             v1.set(colMax, rowMin, lrHeight);
00095             v2.set(colMax, rowMax, urHeight);
00096         }
00097         else
00098         {
00099             //The point lies in the left triangle
00100             v0.set(colMin, rowMin, llHeight);
00101             v1.set(colMax, rowMax, urHeight);
00102             v2.set(colMin, rowMax, ulHeight);
00103         }
00104 
00105         //Compute the normal
00106         osg::Vec3d n = (v1 - v0) ^ (v2 - v0);
00107 
00108         result = ( n.x() * ( c - v0.x() ) + n.y() * ( r - v0.y() ) ) / -n.z() + v0.z();
00109     }
00110     else
00111     {
00112         //OE_INFO << "getHeightAtPixel: (" << c << ", " << r << ")" << std::endl;
00113         int rowMin = osg::maximum((int)floor(r), 0);
00114         int rowMax = osg::maximum(osg::minimum((int)ceil(r), (int)(hf->getNumRows()-1)), 0);
00115         int colMin = osg::maximum((int)floor(c), 0);
00116         int colMax = osg::maximum(osg::minimum((int)ceil(c), (int)(hf->getNumColumns()-1)), 0);
00117 
00118         if (rowMin > rowMax) rowMin = rowMax;
00119         if (colMin > colMax) colMin = colMax;
00120 
00121         float urHeight = hf->getHeight(colMax, rowMax);
00122         float llHeight = hf->getHeight(colMin, rowMin);
00123         float ulHeight = hf->getHeight(colMin, rowMax);
00124         float lrHeight = hf->getHeight(colMax, rowMin);
00125 
00126         //Make sure not to use NoData in the interpolation
00127         if (urHeight == NO_DATA_VALUE || llHeight == NO_DATA_VALUE || ulHeight == NO_DATA_VALUE || lrHeight == NO_DATA_VALUE)
00128         {
00129             return NO_DATA_VALUE;
00130         }
00131 
00132         //OE_INFO << "Heights (ll, lr, ul, ur) ( " << llHeight << ", " << urHeight << ", " << ulHeight << ", " << urHeight << std::endl;
00133 
00134         if (interpolation == INTERP_BILINEAR)
00135         {
00136             //Check for exact value
00137             if ((colMax == colMin) && (rowMax == rowMin))
00138             {
00139                 //OE_NOTICE << "Exact value" << std::endl;
00140                 result = hf->getHeight((int)c, (int)r);
00141             }
00142             else if (colMax == colMin)
00143             {
00144                 //OE_NOTICE << "Vertically" << std::endl;
00145                 //Linear interpolate vertically
00146                 result = ((double)rowMax - r) * llHeight + (r - (double)rowMin) * ulHeight;
00147             }
00148             else if (rowMax == rowMin)
00149             {
00150                 //OE_NOTICE << "Horizontally" << std::endl;
00151                 //Linear interpolate horizontally
00152                 result = ((double)colMax - c) * llHeight + (c - (double)colMin) * lrHeight;
00153             }
00154             else
00155             {
00156                 //OE_NOTICE << "Bilinear" << std::endl;
00157                 //Bilinear interpolate
00158                 float r1 = ((double)colMax - c) * llHeight + (c - (double)colMin) * lrHeight;
00159                 float r2 = ((double)colMax - c) * ulHeight + (c - (double)colMin) * urHeight;
00160 
00161                 //OE_INFO << "r1, r2 = " << r1 << " , " << r2 << std::endl;
00162 
00163                 result = ((double)rowMax - r) * r1 + (r - (double)rowMin) * r2;
00164             }
00165         }
00166         else if (interpolation == INTERP_AVERAGE)
00167         {
00168             double x_rem = c - (int)c;
00169             double y_rem = r - (int)r;
00170 
00171             double w00 = (1.0 - y_rem) * (1.0 - x_rem) * (double)llHeight;
00172             double w01 = (1.0 - y_rem) * x_rem * (double)lrHeight;
00173             double w10 = y_rem * (1.0 - x_rem) * (double)ulHeight;
00174             double w11 = y_rem * x_rem * (double)urHeight;
00175 
00176             result = (float)(w00 + w01 + w10 + w11);
00177         }
00178     }
00179 
00180     return result;
00181 }
00182 
00183 float
00184 HeightFieldUtils::getHeightAtLocation(const osg::HeightField* hf, double x, double y, double llx, double lly, double dx, double dy, ElevationInterpolation interpolation)
00185 {
00186     //Determine the pixel to sample
00187     double px = osg::clampBetween( (x - llx) / dx, 0.0, (double)(hf->getNumColumns()-1) );
00188     double py = osg::clampBetween( (y - lly) / dy, 0.0, (double)(hf->getNumRows()-1) );
00189     return getHeightAtPixel(hf, px, py, interpolation);
00190 }
00191 
00192 float
00193 HeightFieldUtils::getHeightAtNormalizedLocation(const osg::HeightField* input,
00194                                                 double nx, double ny,
00195                                                 ElevationInterpolation interp)
00196 {
00197     double px = nx * (double)(input->getNumColumns() - 1);
00198     double py = ny * (double)(input->getNumRows() - 1);
00199     return getHeightAtPixel( input, px, py, interp );
00200 }
00201 
00202 
00203 void
00204 HeightFieldUtils::scaleHeightFieldToDegrees( osg::HeightField* hf )
00205 {
00206     if (hf)
00207     {
00208         //The number of degrees in a meter at the equator
00209         //TODO: adjust this calculation based on the actual EllipsoidModel.
00210         float scale = 1.0f/111319.0f;
00211 
00212         for (unsigned int i = 0; i < hf->getHeightList().size(); ++i)
00213         {
00214             hf->getHeightList()[i] *= scale;
00215         }
00216     }
00217     else
00218     {
00219         OE_WARN << "[osgEarth::HeightFieldUtils] scaleHeightFieldToDegrees heightfield is NULL" << std::endl;
00220     }
00221 }
00222 
00223 
00224 osg::HeightField*
00225 HeightFieldUtils::createSubSample(osg::HeightField* input, const GeoExtent& inputEx, 
00226                                   const GeoExtent& outputEx, osgEarth::ElevationInterpolation interpolation)
00227 {
00228     double div = outputEx.width()/inputEx.width();
00229     if ( div >= 1.0f )
00230         return 0L;
00231 
00232     int numCols = input->getNumColumns();
00233     int numRows = input->getNumRows();
00234 
00235     //float dx = input->getXInterval() * div;
00236     //float dy = input->getYInterval() * div;
00237 
00238     double xInterval = inputEx.width()  / (double)(input->getNumColumns()-1);
00239     double yInterval = inputEx.height()  / (double)(input->getNumRows()-1);
00240     double dx = div * xInterval;
00241     double dy = div * yInterval;
00242 
00243 
00244     osg::HeightField* dest = new osg::HeightField();
00245     dest->allocate( numCols, numRows );
00246     dest->setXInterval( dx );
00247     dest->setYInterval( dy );
00248 
00249     // copy over the skirt height, adjusting it for relative tile size.
00250     dest->setSkirtHeight( input->getSkirtHeight() * div );
00251 
00252     double x, y;
00253     int col, row;
00254 
00255     for( x = outputEx.xMin(), col=0; col < numCols; x += dx, col++ )
00256     {
00257         for( y = outputEx.yMin(), row=0; row < numRows; y += dy, row++ )
00258         {
00259             float height = HeightFieldUtils::getHeightAtLocation( input, x, y, inputEx.xMin(), inputEx.yMin(), xInterval, yInterval, interpolation);
00260             dest->setHeight( col, row, height );
00261         }
00262     }
00263 
00264     osg::Vec3d orig( outputEx.xMin(), outputEx.yMin(), input->getOrigin().z() );
00265     dest->setOrigin( orig );
00266 
00267     return dest;
00268 }
00269 
00270 osg::HeightField*
00271 HeightFieldUtils::resizeHeightField(osg::HeightField* input, int newColumns, int newRows,
00272                                     ElevationInterpolation interp)
00273 {
00274     if ( newColumns <= 1 && newRows <= 1 )
00275         return 0L;
00276 
00277     if ( newColumns == input->getNumColumns() && newRows == (int)input->getNumRows() )
00278         return new osg::HeightField( *input, osg::CopyOp::DEEP_COPY_ALL );
00279 
00280     double spanX = (input->getNumColumns()-1) * input->getXInterval();
00281     double spanY = (input->getNumRows()-1) * input->getYInterval();
00282     const osg::Vec3& origin = input->getOrigin();
00283 
00284     double stepX = spanX/(double)(newColumns-1);
00285     double stepY = spanY/(double)(newRows-1);
00286 
00287     osg::HeightField* output = new osg::HeightField();
00288     output->allocate( newColumns, newRows );
00289     output->setXInterval( stepX );
00290     output->setYInterval( stepY );
00291     output->setOrigin( origin );
00292     
00293     for( int y = 0; y < newRows; ++y )
00294     {
00295         for( int x = 0; x < newColumns; ++x )
00296         {
00297             double nx = (double)x / (double)(newColumns-1);
00298             double ny = (double)y / (double)(newRows-1);
00299             float h = getHeightAtNormalizedLocation( input, nx, ny );
00300             output->setHeight( x, y, h );
00301         }
00302     }
00303 
00304     return output;
00305 }
00306 
00307 osg::ClusterCullingCallback*
00308 HeightFieldUtils::createClusterCullingCallback( osg::HeightField* grid, osg::EllipsoidModel* et, float verticalScale )
00309 {
00310     //This code is a very slightly modified version of the DestinationTile::createClusterCullingCallback in VirtualPlanetBuilder.
00311     if ( !grid || !et )
00312         return 0L;
00313 
00314     double globe_radius = et->getRadiusPolar();
00315     unsigned int numColumns = grid->getNumColumns();
00316     unsigned int numRows = grid->getNumRows();
00317 
00318     double midLong = grid->getOrigin().x()+grid->getXInterval()*((double)(numColumns-1))*0.5;
00319     double midLat = grid->getOrigin().y()+grid->getYInterval()*((double)(numRows-1))*0.5;
00320     double midZ = grid->getOrigin().z();
00321 
00322     double midX,midY;
00323     et->convertLatLongHeightToXYZ(osg::DegreesToRadians(midLat),osg::DegreesToRadians(midLong),midZ, midX,midY,midZ);
00324 
00325     osg::Vec3 center_position(midX,midY,midZ);
00326     osg::Vec3 center_normal(midX,midY,midZ);
00327     center_normal.normalize();
00328 
00329     osg::Vec3 transformed_center_normal = center_normal;
00330 
00331     unsigned int r,c;
00332 
00333     // populate the vertex/normal/texcoord arrays from the grid.
00334     double orig_X = grid->getOrigin().x();
00335     double delta_X = grid->getXInterval();
00336     double orig_Y = grid->getOrigin().y();
00337     double delta_Y = grid->getYInterval();
00338     double orig_Z = grid->getOrigin().z();
00339 
00340 
00341     float min_dot_product = 1.0f;
00342     float max_cluster_culling_height = 0.0f;
00343     float max_cluster_culling_radius = 0.0f;
00344 
00345     for(r=0;r<numRows;++r)
00346     {
00347         for(c=0;c<numColumns;++c)
00348         {
00349             double X = orig_X + delta_X*(double)c;
00350             double Y = orig_Y + delta_Y*(double)r;
00351             double Z = orig_Z + grid->getHeight(c,r) * verticalScale;
00352             double height = Z;
00353 
00354             et->convertLatLongHeightToXYZ(
00355                 osg::DegreesToRadians(Y), osg::DegreesToRadians(X), Z,
00356                 X, Y, Z);
00357 
00358             osg::Vec3d v(X,Y,Z);
00359             osg::Vec3 dv = v - center_position;
00360             double d = sqrt(dv.x()*dv.x() + dv.y()*dv.y() + dv.z()*dv.z());
00361             double theta = acos( globe_radius/ (globe_radius + fabs(height)) );
00362             double phi = 2.0 * asin (d*0.5/globe_radius); // d/globe_radius;
00363             double beta = theta+phi;
00364             double cutoff = osg::PI_2 - 0.1;
00365 
00366             //log(osg::INFO,"theta="<<theta<<"\tphi="<<phi<<" beta "<<beta);
00367             if (phi<cutoff && beta<cutoff)
00368             {
00369                 float local_dot_product = -sin(theta + phi);
00370                 float local_m = globe_radius*( 1.0/ cos(theta+phi) - 1.0);
00371                 float local_radius = static_cast<float>(globe_radius * tan(beta)); // beta*globe_radius;
00372                 min_dot_product = osg::minimum(min_dot_product, local_dot_product);
00373                 max_cluster_culling_height = osg::maximum(max_cluster_culling_height,local_m);      
00374                 max_cluster_culling_radius = osg::maximum(max_cluster_culling_radius,local_radius);
00375             }
00376             else
00377             {
00378                 //log(osg::INFO,"Turning off cluster culling for wrap around tile.");
00379                 return 0;
00380             }
00381         }
00382     }    
00383 
00384     osg::ClusterCullingCallback* ccc = new osg::ClusterCullingCallback;
00385 
00386     ccc->set(center_position + transformed_center_normal*max_cluster_culling_height ,
00387         transformed_center_normal, 
00388         min_dot_product,
00389         max_cluster_culling_radius);
00390 
00391     return ccc;
00392 }
00393 
00394 /******************************************************************************************/
00395 
00396 ReplaceInvalidDataOperator::ReplaceInvalidDataOperator():
00397 _replaceWith(0.0f)
00398 {
00399 }
00400 
00401 void
00402 ReplaceInvalidDataOperator::operator ()(osg::HeightField *heightField)
00403 {
00404     if (heightField && _validDataOperator.valid())
00405     {
00406         for (unsigned int i = 0; i < heightField->getHeightList().size(); ++i)
00407         {
00408             float elevation = heightField->getHeightList()[i];
00409             if (!(*_validDataOperator)(elevation))
00410             {
00411                 heightField->getHeightList()[i] = _replaceWith;
00412             }
00413         }
00414     }
00415 }
00416 
00417 
00418 /******************************************************************************************/
00419 FillNoDataOperator::FillNoDataOperator():
00420 _defaultValue(0.0f)
00421 {
00422 }
00423 
00424 void
00425 FillNoDataOperator::operator ()(osg::HeightField *heightField)
00426 {
00427     if (heightField && _validDataOperator.valid())
00428     {
00429         for( unsigned int row=0; row < heightField->getNumRows(); row++ )
00430         {
00431             for( unsigned int col=0; col < heightField->getNumColumns(); col++ )
00432             {
00433                 float val = heightField->getHeight(col, row);
00434 
00435                 if (!(*_validDataOperator)(val))
00436                 {
00437                     if ( col > 0 )
00438                         val = heightField->getHeight(col-1,row);
00439                     else if ( col <= heightField->getNumColumns()-1 )
00440                         val = heightField->getHeight(col+1,row);
00441 
00442                     if (!(*_validDataOperator)(val))
00443                     {
00444                         if ( row > 0 )
00445                             val = heightField->getHeight(col, row-1);
00446                         else if ( row < heightField->getNumRows()-1 )
00447                             val = heightField->getHeight(col, row+1);
00448                     }
00449 
00450                     if (!(*_validDataOperator)(val))
00451                     {
00452                         val = _defaultValue;
00453                     }
00454 
00455                     heightField->setHeight( col, row, val );
00456                 }
00457             }
00458         }
00459     }
00460 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines