osgEarth 2.1.1
|
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 }