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