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 #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 }