osgEarth 2.1.1
|
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) |
Definition at line 54 of file HeightFieldUtils.
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; }
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); }
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 ); }
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; }
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; }
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; } }