osgEarth 2.1.1

/home/cube/sources/osgearth/src/osgEarthUtil/ElevationManager.cpp

Go to the documentation of this file.
00001 #include <osgEarthUtil/ElevationManager>
00002 #include <osgEarth/Locators>
00003 #include <osgTerrain/TerrainTile>
00004 #include <osgTerrain/GeometryTechnique>
00005 #include <osgUtil/IntersectionVisitor>
00006 #include <osgUtil/LineSegmentIntersector>
00007 
00008 #define LC "[ElevationManager] "
00009 
00010 using namespace osgEarth;
00011 using namespace osgEarth::Util;
00012 using namespace OpenThreads;
00013 
00014 ElevationManager::ElevationManager( Map* map ) :
00015 _mapf( map, Map::ELEVATION_LAYERS )
00016 {
00017     postCTOR();
00018 }
00019 
00020 void
00021 ElevationManager::postCTOR()
00022 {
00023     _tileSize = 0;
00024     _maxDataLevel = 0;
00025     _maxCacheSize = 100;
00026     _technique = TECHNIQUE_PARAMETRIC;
00027     _interpolation = INTERP_BILINEAR;
00028     _maxLevelOverride = -1;
00029 }
00030 
00031 void
00032 ElevationManager::sync()
00033 {
00034     if ( _mapf.sync() || _tileSize == 0 || _maxDataLevel == 0 )
00035     {
00036         _tileSize = 0;
00037         _maxDataLevel = 0;
00038 
00039         for( ElevationLayerVector::const_iterator i = _mapf.elevationLayers().begin(); i != _mapf.elevationLayers().end(); ++i )
00040         {
00041             // we need the maximum tile size
00042             int layerTileSize = i->get()->getTileSize();
00043             if ( layerTileSize > _tileSize )
00044                 _tileSize = layerTileSize;
00045 
00046             // we also need the maximum available data level.
00047             unsigned int layerMaxDataLevel = i->get()->getMaxDataLevel();
00048             if ( layerMaxDataLevel > _maxDataLevel )
00049                 _maxDataLevel = layerMaxDataLevel;
00050         }
00051     }
00052 }
00053 
00054 ElevationManager::Technique
00055 ElevationManager::getTechnique() const
00056 {
00057     return _technique;
00058 }
00059 
00060 void
00061 ElevationManager::setTechnique( ElevationManager::Technique technique )
00062 {
00063     _technique = technique;
00064 }
00065 
00066 void
00067 ElevationManager::setMaxTilesToCache( int value )
00068 {
00069     _maxCacheSize = value;
00070 }
00071 
00072 int
00073 ElevationManager::getMaxTilesToCache() const
00074 {
00075     return _maxCacheSize;
00076 }
00077 
00078 void
00079 ElevationManager::setInterpolation( ElevationInterpolation interp)
00080 {
00081     _interpolation = interp;
00082 }
00083 
00084 ElevationInterpolation
00085 ElevationManager::getElevationInterpolation() const
00086 {
00087     return _interpolation;
00088 }
00089 
00090 bool
00091 ElevationManager::getElevation(double x, double y,
00092                                double resolution,
00093                                const SpatialReference* srs,
00094                                double& out_elevation,
00095                                double& out_resolution)
00096 {
00097     sync();
00098     return getElevationImpl(x, y, resolution, srs, out_elevation, out_resolution);
00099 }
00100 
00101 bool
00102 ElevationManager::getElevationImpl(double x, double y,
00103                                    double resolution,
00104                                    const SpatialReference* srs,
00105                                    double& out_elevation,
00106                                    double& out_resolution)
00107 {
00108     if ( _maxDataLevel == 0 || _tileSize == 0 )
00109     {
00110         // this means there are no heightfields.
00111         out_elevation = 0.0;
00112         return true;
00113     }
00114    
00115     // this is the ideal LOD for the requested resolution:
00116     unsigned int idealLevel = resolution > 0.0
00117         ? _mapf.getProfile()->getLevelOfDetailForHorizResolution( resolution, _tileSize )
00118         : _maxDataLevel;        
00119 
00120     // based on the heightfields available, this is the best we can theorically do:
00121     unsigned int bestAvailLevel = osg::minimum( idealLevel, _maxDataLevel );
00122     if (_maxLevelOverride >= 0)
00123     {
00124         bestAvailLevel = osg::minimum(bestAvailLevel, (unsigned int)_maxLevelOverride);
00125     }
00126     
00127     // transform the input coords to map coords:
00128     double map_x = x, map_y = y;
00129     if ( srs && !srs->isEquivalentTo( _mapf.getProfile()->getSRS() ) )
00130     {
00131         if ( !srs->transform2D( x, y, _mapf.getProfile()->getSRS(), map_x, map_y ) )
00132         {
00133             OE_WARN << LC << "Fail: coord transform failed" << std::endl;
00134             return false;
00135         }
00136     }
00137 
00138     osg::ref_ptr<osg::HeightField> hf;
00139     osg::ref_ptr<osgTerrain::TerrainTile> tile;
00140 
00141     // get the tilekey corresponding to the tile we need:
00142     TileKey key = _mapf.getProfile()->createTileKey( map_x, map_y, bestAvailLevel );
00143     if ( !key.valid() )
00144     {
00145         OE_WARN << LC << "Fail: coords fall outside map" << std::endl;
00146         return false;
00147     }
00148 
00149     // now, see if we already have this tile loaded somewhere:
00150     osgTerrain::TileID tileId = key.getTileId();
00151 
00152     if ( !tile.valid() )
00153     {
00154         // next check the local tile cache:
00155         TileTable::const_iterator i = _tileCache.find( tileId );
00156         if ( i != _tileCache.end() )
00157             tile = i->second.get();
00158     }
00159 
00160          
00161     // if we found it, make sure it has a heightfield in it:
00162     if ( tile.valid() )
00163     {
00164         osgTerrain::HeightFieldLayer* layer = dynamic_cast<osgTerrain::HeightFieldLayer*>(tile->getElevationLayer());
00165         if ( layer )
00166         {
00167             hf = layer->getHeightField();
00168         }
00169         if ( !hf.valid() )
00170         {
00171             tile = NULL;
00172         }
00173     }
00174 
00175     // if we didn't find it (or it didn't have heightfield data), build it.
00176     if ( !tile.valid() )
00177     {
00178         //OE_NOTICE << "ElevationManager: cache miss" << std::endl;
00179 
00180         // generate the heightfield corresponding to the tile key, automatically falling back
00181         // on lower resolution if necessary:
00182         _mapf.getHeightField( key, true, hf, 0L, _interpolation );
00183 
00184         // bail out if we could not make a heightfield a all.
00185         if ( !hf.valid() )
00186         {
00187             OE_WARN << "ElevationManager: unable to create heightfield" << std::endl;
00188             return false;
00189         }
00190 
00191         GeoLocator* locator = GeoLocator::createForKey( key, _mapf.getMapInfo() );
00192 
00193         tile = new osgTerrain::TerrainTile();
00194 
00195         osgTerrain::HeightFieldLayer* layer = new osgTerrain::HeightFieldLayer( hf.get() );
00196         layer->setLocator( locator );
00197 
00198         tile->setElevationLayer( layer );
00199         tile->setRequiresNormals( false );
00200         tile->setTerrainTechnique( new osgTerrain::GeometryTechnique );
00201 
00202         // store it in the local tile cache.
00203         // TODO: limit the size of the cache with a parallel FIFO list.
00204         _tileCache[tileId] = tile.get();
00205         _tileCacheFIFO.push_back( tileId );
00206 
00207         // prune the cache. this is a terrible pruning method.
00208         if ( _tileCache.size() > _maxCacheSize )
00209         {
00210             osgTerrain::TileID id = _tileCacheFIFO.front();
00211             _tileCacheFIFO.pop_front();
00212             if ( tileId != id )
00213                 _tileCache.erase( id );
00214         }
00215     }
00216 
00217 
00218     // see what the actual resolution of the heightfield is.
00219     out_resolution = (double)hf->getXInterval();
00220 
00221 
00222     // finally it's time to get a height value:
00223     if ( _technique == TECHNIQUE_PARAMETRIC )
00224     {
00225         const GeoExtent& extent = key.getExtent();
00226         double xInterval = extent.width()  / (double)(hf->getNumColumns()-1);
00227         double yInterval = extent.height() / (double)(hf->getNumRows()-1);
00228         out_elevation = (double) HeightFieldUtils::getHeightAtLocation( hf.get(), map_x, map_y, extent.xMin(), extent.yMin(), xInterval, yInterval );
00229         return true;
00230     }
00231     else // ( _technique == TECHNIQUE_GEOMETRIC )
00232     {
00233         osg::Vec3d start, end, zero;
00234 
00235         if ( _mapf.getMapInfo().isGeocentric() )
00236         {
00237             const osg::EllipsoidModel* ellip = _mapf.getProfile()->getSRS()->getEllipsoid();
00238 
00239             ellip->convertLatLongHeightToXYZ(
00240                 osg::DegreesToRadians( map_y ),
00241                 osg::DegreesToRadians( map_x ),
00242                 50000,
00243                 start.x(), start.y(), start.z() );
00244 
00245             ellip->convertLatLongHeightToXYZ(
00246                 osg::DegreesToRadians( map_y ),
00247                 osg::DegreesToRadians( map_x ),
00248                 -50000,
00249                 end.x(), end.y(), end.z() );
00250 
00251             ellip->convertLatLongHeightToXYZ(
00252                 osg::DegreesToRadians( map_y ),
00253                 osg::DegreesToRadians( map_x ),
00254                 0.0,
00255                 zero.x(), zero.y(), zero.z() );
00256         }
00257         else // PROJECTED
00258         {
00259             start.x() = map_x; start.y() = map_y; start.z() = 50000;
00260             end.x() = map_x; end.y() = map_y; end.z() = -50000;
00261             zero.x() = map_x; zero.y() = map_y; zero.z() = 0;
00262         }
00263 
00264         osgUtil::LineSegmentIntersector* i = new osgUtil::LineSegmentIntersector( start, end );
00265         osgUtil::IntersectionVisitor iv;
00266         iv.setIntersector( i );
00267 
00268         tile->accept( iv );
00269 
00270         osgUtil::LineSegmentIntersector::Intersections& results = i->getIntersections();
00271         if ( !results.empty() )
00272         {
00273             const osgUtil::LineSegmentIntersector::Intersection& result = *results.begin();
00274             osg::Vec3d isectPoint = result.getWorldIntersectPoint();
00275             out_elevation = (isectPoint-end).length2() > (zero-end).length2()
00276                 ? (isectPoint-zero).length()
00277                 : -(isectPoint-zero).length();
00278             return true;            
00279         }
00280 
00281         OE_WARN << "ElevationManager: no intersections" << std::endl;
00282         return false;
00283     }
00284 }
00285 
00286 
00287 bool 
00288 ElevationManager::getPlacementMatrix(double x, double y, double z,
00289                                      double resolution,
00290                                      const SpatialReference* srs,
00291                                      osg::Matrixd& out_matrix,
00292                                      double& out_elevation,
00293                                      double& out_resolution)
00294 {
00295     sync();
00296 
00297     const SpatialReference* mapSRS = _mapf.getProfile()->getSRS();
00298 
00299     // transform the input coords to map coords:
00300     double map_x = x, map_y = y;
00301     if ( srs && !srs->isEquivalentTo( mapSRS ) )
00302     {
00303         if ( !srs->transform2D( x, y, mapSRS, map_x, map_y ) )
00304         {
00305             OE_WARN << LC << "getPlacementMatrix: coord transform failed" << std::endl;
00306             return false;
00307         }
00308     }
00309 
00310     // get the elevation under those coordinates:
00311     if ( !getElevationImpl( map_x, map_y, resolution, mapSRS, out_elevation, out_resolution) )
00312     {
00313         OE_WARN << LC << "getPlacementMatrix: getElevation failed" << std::endl;
00314         return false;
00315     }
00316 
00317     if ( _mapf.getMapInfo().isGeocentric() )
00318     {
00319         mapSRS->getEllipsoid()->computeLocalToWorldTransformFromLatLongHeight(
00320             osg::DegreesToRadians( map_y ),
00321             osg::DegreesToRadians( map_x ),
00322             out_elevation + z,
00323             out_matrix );
00324     }
00325     else
00326     {
00327         out_matrix = osg::Matrixd::translate( x, y, out_elevation + z );
00328     }
00329 
00330     return true;
00331 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines