osgEarth 2.1.1

/home/cube/sources/osgearth/src/osgEarth/ElevationLayer.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 #include <osgEarth/ElevationLayer>
00020 #include <osgEarth/Registry>
00021 #include <osg/Version>
00022 
00023 using namespace osgEarth;
00024 using namespace OpenThreads;
00025 
00026 #define LC "[ElevationLayer] "
00027 
00028 //------------------------------------------------------------------------
00029 
00030 ElevationLayerOptions::ElevationLayerOptions( const ConfigOptions& options ) :
00031 TerrainLayerOptions( options )
00032 {
00033     setDefaults();
00034     fromConfig( _conf );
00035 }
00036 
00037 ElevationLayerOptions::ElevationLayerOptions( const std::string& name, const TileSourceOptions& driverOptions ) :
00038 TerrainLayerOptions( name, driverOptions )
00039 {
00040     setDefaults();
00041     fromConfig( _conf );
00042 }
00043 
00044 void
00045 ElevationLayerOptions::setDefaults()
00046 {
00047     //NOP
00048 }
00049 
00050 Config
00051 ElevationLayerOptions::getConfig() const
00052 {
00053     Config conf = TerrainLayerOptions::getConfig();
00054     //NOP
00055     return conf;
00056 }
00057 
00058 void
00059 ElevationLayerOptions::fromConfig( const Config& conf )
00060 {
00061     //NOP
00062 }
00063 
00064 void
00065 ElevationLayerOptions::mergeConfig( const Config& conf )
00066 {
00067     TerrainLayerOptions::mergeConfig( conf );
00068     fromConfig( conf );
00069 }
00070 
00071 //------------------------------------------------------------------------
00072 
00073 namespace
00074 {
00075     struct ElevationLayerPreCacheOperation : public TileSource::HeightFieldOperation
00076     {
00077         osg::ref_ptr<CompositeValidValueOperator> ops;
00078 
00079         ElevationLayerPreCacheOperation( TileSource* source )
00080         {
00081                     ops = new CompositeValidValueOperator;
00082                     ops->getOperators().push_back(new osgTerrain::NoDataValue(source->getNoDataValue()));
00083                     ops->getOperators().push_back(new osgTerrain::ValidRange(source->getNoDataMinValue(), source->getNoDataMaxValue()));
00084         }
00085 
00086         void operator()( osg::ref_ptr<osg::HeightField>& hf )
00087         {
00088                     //Modify the heightfield data so that is contains a standard value for NO_DATA
00089                     ReplaceInvalidDataOperator op;
00090                     op.setReplaceWith(NO_DATA_VALUE);
00091                     op.setValidDataOperator(ops.get());
00092                     op( hf.get() );
00093         }
00094     };
00095 }
00096 
00097 //------------------------------------------------------------------------
00098 
00099 ElevationLayer::ElevationLayer( const ElevationLayerOptions& options ) :
00100 TerrainLayer   ( &_runtimeOptions ),
00101 _runtimeOptions( options )
00102 {
00103     init();
00104 }
00105 
00106 ElevationLayer::ElevationLayer( const std::string& name, const TileSourceOptions& driverOptions ) :
00107 TerrainLayer   ( &_runtimeOptions ),
00108 _runtimeOptions( ElevationLayerOptions(name, driverOptions) )
00109 {
00110     init();
00111 }
00112 
00113 ElevationLayer::ElevationLayer( const ElevationLayerOptions& options, TileSource* tileSource ) :
00114 TerrainLayer   ( &_runtimeOptions, tileSource ),
00115 _runtimeOptions( options )
00116 {
00117     init();
00118 }
00119 
00120 void
00121 ElevationLayer::init()
00122 {
00123     _tileSize = 32;
00124 }
00125 
00126 std::string
00127 ElevationLayer::suggestCacheFormat() const
00128 {
00129 #if OSG_MIN_VERSION_REQUIRED(2,8,0)
00130         //OSG 2.8 onwards should use TIF for heightfields
00131         return "tif";
00132 #else
00133         //OSG 2.8 and below should use DDS
00134         return "dds";
00135 #endif
00136 }
00137 
00138 void
00139 ElevationLayer::addCallback( ElevationLayerCallback* cb )
00140 {
00141     _callbacks.push_back( cb );
00142 }
00143 
00144 void
00145 ElevationLayer::removeCallback( ElevationLayerCallback* cb )
00146 {
00147     ElevationLayerCallbackList::iterator i = std::find( _callbacks.begin(), _callbacks.end(), cb );
00148     if ( i != _callbacks.end() ) 
00149         _callbacks.erase( i );
00150 }
00151 
00152 void
00153 ElevationLayer::fireCallback( TerrainLayerCallbackMethodPtr method )
00154 {
00155     for( ElevationLayerCallbackList::const_iterator i = _callbacks.begin(); i != _callbacks.end(); ++i )
00156     {
00157         ElevationLayerCallback* cb = i->get();
00158         (cb->*method)( this );
00159     }
00160 }
00161 
00162 void
00163 ElevationLayer::fireCallback( ElevationLayerCallbackMethodPtr method )
00164 {
00165     for( ElevationLayerCallbackList::const_iterator i = _callbacks.begin(); i != _callbacks.end(); ++i )
00166     {
00167         ElevationLayerCallback* cb = i->get();
00168         (cb->*method)( this );
00169     }
00170 }
00171 
00172 void
00173 ElevationLayer::initTileSource()
00174 {
00175     // call superclass first.
00176     TerrainLayer::initTileSource();
00177 
00178     if ( _tileSource.valid() )
00179         _preCacheOp = new ElevationLayerPreCacheOperation( _tileSource.get() );
00180 }
00181 
00182 GeoHeightField
00183 ElevationLayer::createGeoHeightField(const TileKey& key, ProgressCallback* progress)
00184 {
00185     osg::HeightField* hf = 0L;
00186     //osg::ref_ptr<osg::HeightField> hf;
00187 
00188     TileSource* source = getTileSource();
00189 
00190     //Only try to get the tile if it isn't blacklisted
00191     if (!source->getBlacklist()->contains( key.getTileId() ))
00192     {
00193         //Only try to get data if the source actually has data
00194         if (source->hasData( key ) )
00195         {
00196             hf = source->createHeightField( key, _preCacheOp.get(), progress );
00197 
00198             //Blacklist the tile if we can't get it and it wasn't cancelled
00199             if ( !hf && (!progress || !progress->isCanceled()))
00200             {
00201                 source->getBlacklist()->add(key.getTileId());
00202             }
00203         }
00204         else
00205         {
00206             OE_DEBUG << LC << "Source for layer \"" << getName() << "\" has no data at " << key.str() << std::endl;
00207         }
00208     }
00209     else
00210     {
00211         OE_DEBUG << LC << "Tile " << key.str() << " is blacklisted " << std::endl;
00212     }
00213 
00214     return hf ?
00215         GeoHeightField( hf, key.getExtent(), getProfile()->getVerticalSRS() ) :
00216         GeoHeightField::INVALID;
00217 }
00218 
00219 osg::HeightField*
00220 ElevationLayer::createHeightField(const osgEarth::TileKey& key, ProgressCallback* progress )
00221 {
00222     osg::HeightField* result = 0L;
00223     //osg::ref_ptr<osg::HeightField> result;
00224 
00225     const Profile* layerProfile = getProfile();
00226     const Profile* mapProfile = key.getProfile();
00227 
00228         if ( !layerProfile )
00229         {
00230                 OE_WARN << LC << "Could not get a valid profile for Layer \"" << getName() << "\"" << std::endl;
00231         return 0L;
00232         }
00233 
00234         if ( !isCacheOnly() && !getTileSource() )
00235         {
00236                 OE_WARN << LC << "Error: ElevationLayer does not have a valid TileSource, cannot create heightfield " << std::endl;
00237                 return 0L;
00238         }
00239 
00240     //Write the layer properties if they haven't been written yet.  Heightfields are always stored in the map profile.
00241     if (!_cacheProfile.valid() && _cache.valid() && _runtimeOptions.cacheEnabled() == true && _tileSource.valid())
00242     {
00243         _cacheProfile = mapProfile;
00244         if ( _tileSource->isOK() )
00245         {
00246             _cache->storeProperties( _cacheSpec, _cacheProfile.get(),  _tileSource->getPixelsPerTile() );
00247         }
00248     }
00249 
00250         //See if we can get it from the cache.
00251         if (_cache.valid() && _runtimeOptions.cacheEnabled() == true )
00252         {
00253         osg::ref_ptr<const osg::HeightField> cachedHF;
00254                 if ( _cache->getHeightField( key, _cacheSpec, cachedHF ) )
00255                 {
00256                         OE_DEBUG << LC << "ElevationLayer::createHeightField got tile " << key.str() << " from layer \"" << getName() << "\" from cache " << std::endl;
00257 
00258             // make a copy:
00259             result = new osg::HeightField( *cachedHF.get() );
00260                 }
00261         }
00262 
00263     //in cache-only mode, if the cache fetch failed, bail out.
00264     if ( result == 0L && isCacheOnly() )
00265     {
00266         return 0L;
00267     }
00268 
00269         if ( result == 0L && getTileSource() && getTileSource()->isOK() )
00270     {
00271                 //If the profiles are equivalent, get the HF from the TileSource.
00272                 if (key.getProfile()->isEquivalentTo( getProfile() ))
00273                 {
00274                         if (isKeyValid( key ) )
00275                         {
00276                                 GeoHeightField hf = createGeoHeightField( key, progress );
00277                                 if (hf.valid())
00278                                 {
00279                                         result = hf.takeHeightField();
00280                                 }
00281                         }
00282                 }
00283 
00284                 else
00285                 {
00286                         //Collect the heightfields for each of the intersecting tiles.
00287                         //typedef std::vector< GeoHeightField > HeightFields;
00288                         GeoHeightFieldVector heightFields;
00289 
00290                         //Determine the intersecting keys
00291                         std::vector< TileKey > intersectingTiles;
00292                         getProfile()->getIntersectingTiles(key, intersectingTiles);
00293                         if (intersectingTiles.size() > 0)
00294                         {
00295                                 for (unsigned int i = 0; i < intersectingTiles.size(); ++i)
00296                                 {
00297                                         if (isKeyValid( intersectingTiles[i] ) )
00298                                         {
00299                         GeoHeightField hf = createGeoHeightField( intersectingTiles[i], progress );
00300                                                 if (hf.valid())
00301                                                 {
00302                                                         heightFields.push_back(hf);
00303                                                 }
00304                                         }
00305                                 }
00306                         }
00307 
00308                         //If we actually got a HeightField, resample/reproject it to match the incoming TileKey's extents.
00309                         if (heightFields.size() > 0)
00310                         {               
00311                                 unsigned int width = 0;
00312                                 unsigned int height = 0;
00313 
00314                 for (GeoHeightFieldVector::iterator itr = heightFields.begin(); itr != heightFields.end(); ++itr)
00315                                 {
00316                                         if (itr->getHeightField()->getNumColumns() > width)
00317                         width = itr->getHeightField()->getNumColumns();
00318                                         if (itr->getHeightField()->getNumRows() > height) 
00319                         height = itr->getHeightField()->getNumRows();
00320                                 }
00321 
00322                 result = new osg::HeightField();
00323                                 result->allocate(width, height);
00324 
00325                                 //Go ahead and set up the heightfield so we don't have to worry about it later
00326                                 double minx, miny, maxx, maxy;
00327                                 key.getExtent().getBounds(minx, miny, maxx, maxy);
00328                                 double dx = (maxx - minx)/(double)(width-1);
00329                                 double dy = (maxy - miny)/(double)(height-1);
00330 
00331                                 //Create the new heightfield by sampling all of them.
00332                                 for (unsigned int c = 0; c < width; ++c)
00333                                 {
00334                                         double geoX = minx + (dx * (double)c);
00335                                         for (unsigned r = 0; r < height; ++r)
00336                                         {
00337                                                 double geoY = miny + (dy * (double)r);
00338 
00339                                                 //For each sample point, try each heightfield.  The first one with a valid elevation wins.
00340                                                 float elevation = NO_DATA_VALUE;
00341                                                 for (GeoHeightFieldVector::iterator itr = heightFields.begin(); itr != heightFields.end(); ++itr)
00342                                                 {
00343                                                         float e = 0.0;
00344                             if (itr->getElevation(key.getExtent().getSRS(), geoX, geoY, INTERP_BILINEAR, _profile->getVerticalSRS(), e))
00345                                                         {
00346                                                                 elevation = e;
00347                                                                 break;
00348                                                         }
00349                                                 }
00350                                                 result->setHeight( c, r, elevation );                
00351                                         }
00352                                 }
00353                         }
00354                 }
00355     
00356         //Write the result to the cache.
00357         if (result && _cache.valid() && _runtimeOptions.cacheEnabled() == true )
00358         {
00359             _cache->setHeightField( key, _cacheSpec, result );
00360         }
00361     }
00362 
00363         //Initialize the HF values for osgTerrain
00364         if ( result )
00365         {       
00366                 //Go ahead and set up the heightfield so we don't have to worry about it later
00367                 double minx, miny, maxx, maxy;
00368                 key.getExtent().getBounds(minx, miny, maxx, maxy);
00369                 result->setOrigin( osg::Vec3d( minx, miny, 0.0 ) );
00370                 double dx = (maxx - minx)/(double)(result->getNumColumns()-1);
00371                 double dy = (maxy - miny)/(double)(result->getNumRows()-1);
00372                 result->setXInterval( dx );
00373                 result->setYInterval( dy );
00374                 result->setBorderWidth( 0 );
00375         }
00376         
00377     return result;
00378 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines