osgEarth 2.1.1

/home/cube/sources/osgearth/src/osgEarthFeatures/FeatureModelGraph.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 
00020 #include <osgEarthFeatures/FeatureModelGraph>
00021 #include <osgEarthFeatures/CropFilter>
00022 #include <osgEarth/ThreadingUtils>
00023 #include <osgEarth/NodeUtils>
00024 #include <osgEarth/ElevationQuery>
00025 #include <osg/PagedLOD>
00026 #include <osg/ProxyNode>
00027 #include <osgDB/FileNameUtils>
00028 #include <osgDB/ReaderWriter>
00029 #include <osgDB/WriteFile>
00030 #include <osgUtil/Optimizer>
00031 
00032 #define LC "[FeatureModelGraph] "
00033 
00034 using namespace osgEarth;
00035 using namespace osgEarth::Features;
00036 using namespace osgEarth::Symbology;
00037 
00038 #undef USE_PROXY_NODE_FOR_TESTING
00039 
00040 //---------------------------------------------------------------------------
00041 
00042 // pseudo-loader for paging in feature tiles for a FeatureModelGraph.
00043 
00044 namespace
00045 {
00046     UID                               _uid         = 0;
00047     Threading::ReadWriteMutex         _fmgMutex;
00048     std::map<UID, FeatureModelGraph*> _fmgRegistry;
00049 
00050     static std::string s_makeURI( UID uid, unsigned lod, unsigned x, unsigned y ) 
00051     {
00052         std::stringstream buf;
00053         buf << uid << "." << lod << "_" << x << "_" << y << ".osgearth_pseudo_fmg";
00054         std::string str = buf.str();
00055         return str;
00056     }
00057 
00058     osg::Group* createPagedNode( const osg::BoundingSphered& bs, const std::string& uri, float minRange, float maxRange )
00059     {
00060 #ifdef USE_PROXY_NODE_FOR_TESTING
00061         osg::ProxyNode* p = new osg::ProxyNode();
00062         p->setCenter( bs.center() );
00063         p->setRadius( bs.radius() );
00064         p->setFileName( 0, uri );
00065 #else
00066         osg::PagedLOD* p = new osg::PagedLOD();
00067         p->setCenter( bs.center() );
00068         p->setRadius( bs.radius() );
00069         p->setFileName( 0, uri );
00070         p->setRange( 0, minRange, maxRange );
00071 #endif
00072         return p;
00073     }
00074 }
00075 
00076 struct osgEarthFeatureModelPseudoLoader : public osgDB::ReaderWriter
00077 {
00078     osgEarthFeatureModelPseudoLoader()
00079     {
00080         supportsExtension( "osgearth_pseudo_fmg", "Feature model pseudo-loader" );
00081     }
00082 
00083     const char* className()
00084     { // override
00085         return "osgEarth Feature Model Pseudo-Loader";
00086     }
00087 
00088     ReadResult readNode(const std::string& uri, const Options* options) const
00089     {
00090         if ( !acceptsExtension( osgDB::getLowerCaseFileExtension(uri) ) )
00091             return ReadResult::FILE_NOT_HANDLED;
00092 
00093         UID uid;
00094         unsigned lod, x, y;
00095         sscanf( uri.c_str(), "%u.%d_%d_%d.%*s", &uid, &lod, &x, &y );
00096 
00097         FeatureModelGraph* graph = getGraph(uid);
00098         if ( graph )
00099             return ReadResult( graph->load( lod, x, y, uri ) );
00100         else
00101             return ReadResult::ERROR_IN_READING_FILE;
00102     }
00103 
00104     static UID registerGraph( FeatureModelGraph* graph )
00105     {
00106         Threading::ScopedWriteLock lock( _fmgMutex );
00107         UID key = ++_uid;
00108         _fmgRegistry[key] = graph;
00109         return key;
00110     }
00111 
00112     static void unregisterGraph( UID uid )
00113     {
00114         Threading::ScopedWriteLock lock( _fmgMutex );
00115         _fmgRegistry.erase( uid );
00116     }
00117 
00118     static FeatureModelGraph* getGraph( UID uid ) 
00119     {
00120         Threading::ScopedReadLock lock( _fmgMutex );
00121         std::map<UID, FeatureModelGraph*>::const_iterator i = _fmgRegistry.find( uid );
00122         return i != _fmgRegistry.end() ? i->second : 0L;
00123     }
00124 };
00125 
00126 REGISTER_OSGPLUGIN(osgearth_pseudo_fmg, osgEarthFeatureModelPseudoLoader);
00127 
00128 namespace
00129 {    
00130     GeoExtent
00131     s_getTileExtent( unsigned lod, unsigned tileX, unsigned tileY, const GeoExtent& fullExtent )
00132     {
00133         double w = fullExtent.width();
00134         double h = fullExtent.height();
00135         for( unsigned i=0; i<lod; ++i ) {
00136             w *= 0.5;
00137             h *= 0.5;
00138         }
00139         return GeoExtent(
00140             fullExtent.getSRS(),
00141             fullExtent.xMin() + w * (double)tileX,
00142             fullExtent.yMin() + h * (double)tileY,
00143             fullExtent.xMin() + w * (double)(tileX+1),
00144             fullExtent.yMin() + h * (double)(tileY+1) );
00145     }
00146 }
00147 
00148 
00149 //---------------------------------------------------------------------------
00150 
00151 FeatureModelGraph::FeatureModelGraph(FeatureSource*                   source,
00152                                      const FeatureModelSourceOptions& options,
00153                                      FeatureNodeFactory*              factory,
00154                                      Session*                         session) :
00155 _source   ( source ),
00156 _options  ( options ),
00157 _factory  ( factory ),
00158 _session  ( session ),
00159 _dirty    ( false )
00160 {
00161     _uid = osgEarthFeatureModelPseudoLoader::registerGraph( this );
00162 
00163     // install the stylesheet in the session if it doesn't already have one.
00164     if ( !session->styles() )
00165         session->setStyles( _options.styles().get() );
00166 
00167     // initialize lighting on the graph, if necessary.
00168     osg::StateSet* stateSet = getOrCreateStateSet();
00169 
00170     if ( _options.enableLighting().isSet() )
00171         stateSet->setMode( GL_LIGHTING, *_options.enableLighting() ? 1 : 0 );
00172     
00173     // Calculate the usable extent (in both feature and map coordinates) and bounds.
00174     const Profile* mapProfile = session->getMapInfo().getProfile();
00175 
00176     // the part of the feature extent that will fit on the map (in map coords):
00177     _usableMapExtent = mapProfile->clampAndTransformExtent( 
00178         _source->getFeatureProfile()->getExtent(), 
00179         &_featureExtentClamped );
00180 
00181     // same, back into feature coords:
00182     _usableFeatureExtent = _usableMapExtent.transform( _source->getFeatureProfile()->getSRS() );
00183 
00184     // world-space bounds of the feature layer
00185     _fullWorldBound = getBoundInWorldCoords( _usableMapExtent, 0L );
00186 
00187     // whether to request tiles from the source (if available). if the source is tiled, but the
00188     // user manually specified schema levels, don't use the tiles.
00189     _useTiledSource = _source->getFeatureProfile()->getTiled();
00190 
00191     if ( options.levels().isSet() && options.levels()->getNumLevels() > 0 )
00192     {
00193         // the user provided a custom levels setup, so don't use the tiled source (which
00194         // provides its own levels setup)
00195         _useTiledSource = false;
00196 
00197         // for each custom level, calculate the best LOD match and store it in the level
00198         // layout data. We will use this information later when constructing the SG in
00199         // the pager.
00200         for( unsigned i = 0; i < options.levels()->getNumLevels(); ++i )
00201         {
00202             const FeatureLevel* level = options.levels()->getLevel( i );
00203             unsigned lod = options.levels()->chooseLOD( *level, _fullWorldBound.radius() );
00204             _lodmap.resize( lod+1, 0L );
00205             _lodmap[lod] = level;
00206 
00207             OE_INFO << LC << source->getName() 
00208                 << ": F.Level max=" << level->maxRange() << ", min=" << level->minRange()
00209                 << ", LOD=" << lod << std::endl;
00210         }
00211     }
00212 
00213     setNumChildrenRequiringUpdateTraversal( 1 );
00214 
00215     redraw();
00216 }
00217 
00218 FeatureModelGraph::~FeatureModelGraph()
00219 {
00220     osgEarthFeatureModelPseudoLoader::unregisterGraph( _uid );
00221 }
00222 
00223 void
00224 FeatureModelGraph::dirty()
00225 {
00226     _dirty = true;
00227 }
00228 
00229 osg::BoundingSphered
00230 FeatureModelGraph::getBoundInWorldCoords(const GeoExtent& extent,
00231                                          const MapFrame*  mapf ) const
00232 {
00233     osg::Vec3d center, corner;
00234     double z = 0.0;
00235     GeoExtent workingExtent;
00236 
00237     if ( extent.getSRS()->isEquivalentTo( _usableMapExtent.getSRS() ) )
00238     {
00239         workingExtent = extent;
00240     }
00241     else
00242     {
00243         workingExtent = extent.transform( _usableMapExtent.getSRS() ); // safe.
00244     }
00245 
00246     workingExtent.getCentroid( center.x(), center.y() );
00247     
00248     if ( mapf )
00249     {
00250         // note: use the lowest possible resolution to speed up queries
00251         ElevationQuery query( *mapf );
00252         query.getElevation( center, mapf->getProfile()->getSRS(), center.z(), DBL_MAX );
00253     }
00254 
00255     corner.x() = workingExtent.xMin();
00256     corner.y() = workingExtent.yMin();
00257     corner.z() = z;
00258 
00259     if ( _session->getMapInfo().isGeocentric() )
00260     {
00261         workingExtent.getSRS()->transformToECEF( center, center );
00262         workingExtent.getSRS()->transformToECEF( corner, corner );
00263     }
00264 
00265     return osg::BoundingSphered( center, (center-corner).length() );
00266 }
00267 
00268 void
00269 FeatureModelGraph::setupPaging()
00270 {
00271     // calculate the bounds of the full data extent:
00272     MapFrame mapf = _session->createMapFrame();
00273     osg::BoundingSphered bs = getBoundInWorldCoords( _usableMapExtent, &mapf );
00274 
00275     // calculate the max range for the top-level PLOD:
00276     float maxRange = bs.radius() * _options.levels()->tileSizeFactor().value();
00277 
00278     // build the URI for the top-level paged LOD:
00279     std::string uri = s_makeURI( _uid, 0, 0, 0 );
00280 
00281     // bulid the top level Paged LOD:
00282     osg::Group* pagedNode = createPagedNode( bs, uri, 0.0f, maxRange );
00283     this->addChild( pagedNode );
00284 }
00285 
00286 osg::Node*
00287 FeatureModelGraph::load( unsigned lod, unsigned tileX, unsigned tileY, const std::string& uri )
00288 {
00289     OE_DEBUG << LC
00290         << "load: " << lod << "_" << tileX << "_" << tileY << std::endl;
00291 
00292     osg::Group* result = 0L;
00293     
00294     if ( _useTiledSource )
00295     {        
00296         // A "tiled" source has a pre-generted tile hierarchy, but no range information.
00297         // We will be calculating the LOD ranges here.
00298 
00299         // The extent of this tile:
00300         GeoExtent tileExtent = s_getTileExtent( lod, tileX, tileY, _usableFeatureExtent );
00301 
00302         // Calculate the bounds of this new tile:
00303         MapFrame mapf = _session->createMapFrame();
00304         osg::BoundingSphered tileBound = getBoundInWorldCoords( tileExtent, &mapf );
00305 
00306         // Apply the tile range multiplier to calculate a max camera range. The max range is
00307         // the geographic radius of the tile times the multiplier.
00308         float tileFactor = _options.levels().isSet() ? _options.levels()->tileSizeFactor().get() : 15.0f;
00309         double maxRange =  tileBound.radius() * tileFactor;
00310         FeatureLevel level( 0, maxRange );
00311         
00312         // Construct a tile key that will be used to query the source for this tile.
00313         TileKey key(lod, tileX, tileY, _source->getFeatureProfile()->getProfile());
00314         osg::Group* geometry = build( level, tileExtent, &key );
00315         result = geometry;
00316 
00317         if (lod < _source->getFeatureProfile()->getMaxLevel())
00318         {
00319             // see if there are any more levels. If so, build some pagedlods to bring the
00320             // next level in.
00321             FeatureLevel nextLevel(0, maxRange/2.0);
00322 
00323             osg::ref_ptr<osg::Group> group = new osg::Group();
00324 
00325             // calculate the LOD of the next level:
00326             if ( lod+1 != ~0 )
00327             {
00328                 MapFrame mapf = _session->createMapFrame();
00329                 buildSubTilePagedLODs( lod, tileX, tileY, &mapf, group.get() );
00330 
00331                 // slap the geometry in there afterwards, if there is any
00332                 if ( geometry )
00333                     group->addChild( geometry );
00334 
00335                 result = group.release();
00336             }   
00337         }
00338     }
00339 
00340     else if ( !_options.levels().isSet() || _options.levels()->getNumLevels() == 0 )
00341     {
00342         // This is a non-tiled data source that has NO level details. In this case, 
00343         // we simply want to load all features at once and make them visible at
00344         // maximum camera range.
00345         FeatureLevel all( 0.0f, FLT_MAX );
00346         result = build( all, GeoExtent::INVALID, 0 );
00347     }
00348 
00349     else if ( lod < _lodmap.size() )
00350     {
00351         // This path computes the SG for a model graph with explicity-defined levels of
00352         // detail. We already calculated the LOD level map in setupPaging(). If the
00353         // current LOD points to an actual FeatureLevel, we build the geometry for that
00354         // level in the tile.
00355 
00356         osg::Group* geometry = 0L;
00357         const FeatureLevel* level = _lodmap[lod];
00358         if ( level )
00359         {
00360             // There exists a real data level at this LOD. So build the geometry that will
00361             // represent this tile.
00362             GeoExtent tileExtent = 
00363                 lod > 0 ?
00364                 s_getTileExtent( lod, tileX, tileY, _usableFeatureExtent ) :
00365                 _usableFeatureExtent;
00366 
00367             geometry = build( *level, tileExtent, 0 );
00368             result = geometry;
00369         }
00370 
00371         if ( lod < _lodmap.size()-1 )
00372         {
00373             // There are more populated levels below this one. So build the subtile
00374             // PagedLODs that will load them.
00375             osg::ref_ptr<osg::Group> group = new osg::Group();
00376 
00377             MapFrame mapf = _session->createMapFrame();
00378             buildSubTilePagedLODs( lod, tileX, tileY, &mapf, group.get() );
00379 
00380             if ( geometry )
00381                 group->addChild( geometry );
00382 
00383             result = group.release();
00384         }
00385     }
00386 
00387     if ( !result )
00388     {
00389         // If the read resulting in nothing, create an empty group so that the read
00390         // (technically) succeeds and the pager won't try to load the null child
00391         // over and over.
00392         result = new osg::Group();
00393     }
00394     else
00395     {
00396         RemoveEmptyGroupsVisitor::run( result );
00397     }
00398 
00399     if ( result->getNumChildren() == 0 )
00400     {
00401         // if the result group contains no data, blacklist it so we never try to load it again.
00402         Threading::ScopedWriteLock exclusiveLock( _blacklistMutex );
00403         _blacklist.insert( uri );
00404         OE_DEBUG << LC << "Blacklisting: " << uri << std::endl;
00405     }
00406 
00407     return result;
00408 }
00409 
00410 
00411 void
00412 FeatureModelGraph::buildSubTilePagedLODs(unsigned        parentLOD,
00413                                          unsigned        parentTileX,
00414                                          unsigned        parentTileY,
00415                                          const MapFrame* mapf,
00416                                          osg::Group*     parent)
00417 {
00418     unsigned subtileLOD = parentLOD + 1;
00419     unsigned subtileX = parentTileX * 2;
00420     unsigned subtileY = parentTileY * 2;
00421 
00422     // make a paged LOD for each subtile:
00423     for( unsigned u = subtileX; u <= subtileX + 1; ++u )
00424     {
00425         for( unsigned v = subtileY; v <= subtileY + 1; ++v )
00426         {
00427             GeoExtent subtileFeatureExtent = s_getTileExtent( subtileLOD, u, v, _usableFeatureExtent );
00428             osg::BoundingSphered subtile_bs = getBoundInWorldCoords( subtileFeatureExtent, mapf );
00429 
00430             // Camera range for the PLODs. This should always be sufficient because
00431             // the max range of a FeatureLevel below this will, by definition, have a max range
00432             // less than or equal to this number -- based on how the LODs were chosen in 
00433             // setupPaging.
00434             float maxRange = subtile_bs.radius() * _options.levels()->tileSizeFactor().value();
00435 
00436             std::string uri = s_makeURI( _uid, subtileLOD, u, v );
00437 
00438             // check the blacklist to make sure we haven't unsuccessfully tried
00439             // this URI before
00440             bool blacklisted = false;
00441             {
00442                 Threading::ScopedReadLock sharedLock( _blacklistMutex );
00443                 blacklisted = _blacklist.find( uri ) != _blacklist.end();
00444             }
00445 
00446             if ( !blacklisted )
00447             {
00448                 OE_DEBUG << LC << "    " << uri
00449                     << std::fixed
00450                     << "; center = " << subtile_bs.center().x() << "," << subtile_bs.center().y() << "," << subtile_bs.center().z()
00451                     << "; radius = " << subtile_bs.radius()
00452                     << std::endl;
00453 
00454                 osg::Group* pagedNode = createPagedNode( subtile_bs, uri, 0.0f, maxRange );
00455                 parent->addChild( pagedNode );
00456             }
00457         }
00458     }
00459 }
00460 
00461 osg::Group*
00462 FeatureModelGraph::build( const FeatureLevel& level, const GeoExtent& extent, const TileKey* key )
00463 {
00464     osg::ref_ptr<osg::Group> group = new osg::Group();
00465 
00466     // form the baseline query, which does a spatial query based on the working extent.
00467     Query query;
00468     if ( extent.isValid() )
00469         query.bounds() = extent.bounds();
00470 
00471     // add a tile key to the query if there is one, to support TFS-style queries
00472     if ( key )
00473         query.tileKey() = *key;
00474 
00475     // now, go through any level-based selectors.
00476     const StyleSelectorVector& levelSelectors = level.selectors();
00477     
00478     // if there are none, just build once with the default style and query.
00479     if ( levelSelectors.size() == 0 )
00480     {
00481         // attempt to glean the style from the feature source name:
00482         const Style style = *_session->styles()->getStyle( *_source->getFeatureSourceOptions().name() );
00483 
00484         osg::Node* node = build( style, query, extent );
00485         if ( node )
00486             group->addChild( node );
00487     }
00488 
00489     else
00490     {
00491         for( StyleSelectorVector::const_iterator i = levelSelectors.begin(); i != levelSelectors.end(); ++i )
00492         {
00493             const StyleSelector& selector = *i;
00494 
00495             // fetch the selector's style:
00496             const Style* selectorStyle = _session->styles()->getStyle( selector.getSelectedStyleName() );
00497 
00498             // combine the selector's query, if it has one:
00499             Query selectorQuery = 
00500                 selector.query().isSet() ? query.combineWith( *selector.query() ) : query;
00501 
00502             osg::Node* node = build( *selectorStyle, selectorQuery, extent );
00503             if ( node )
00504                 group->addChild( node );
00505         }
00506     }
00507 
00508     if ( group->getNumChildren() > 0 )
00509     {
00510         // account for a min-range here.
00511         if ( level.minRange() > 0.0f )
00512         {
00513             osg::LOD* lod = new osg::LOD();
00514             lod->addChild( group.get(), level.minRange(), FLT_MAX );
00515             group = lod;
00516         }
00517 
00518         if ( _session->getMapInfo().isGeocentric() && _options.clusterCulling() == true )
00519         {
00520             const GeoExtent& ccExtent = extent.isValid() ? extent : _source->getFeatureProfile()->getExtent();
00521             if ( ccExtent.isValid() )
00522             {
00523                 // if the extent is more than 90 degrees, bail
00524                 GeoExtent geodeticExtent = ccExtent.transform( ccExtent.getSRS()->getGeographicSRS() );
00525                 if ( geodeticExtent.width() < 90.0 && geodeticExtent.height() < 90.0 )
00526                 {
00527 #if 1
00528                     // get the geocentric tile center:
00529                     osg::Vec3d tileCenter;
00530                     ccExtent.getCentroid( tileCenter.x(), tileCenter.y() );
00531                     osg::Vec3d centerECEF;
00532                     ccExtent.getSRS()->transformToECEF( tileCenter, centerECEF );
00533 
00534                     osg::NodeCallback* ccc = ClusterCullerFactory::create( group.get(), centerECEF );
00535                     if ( ccc )
00536                         group->addCullCallback( ccc );
00537 #endif
00538                 }
00539             }
00540         }
00541 
00542         return group.release();
00543     }
00544 
00545     else
00546     {
00547         return 0L;
00548     }
00549 }
00550 
00551 osg::Group*
00552 FeatureModelGraph::build( const Style& baseStyle, const Query& baseQuery, const GeoExtent& workingExtent )
00553 {
00554     osg::ref_ptr<osg::Group> group = new osg::Group();
00555 
00556     if ( _source->hasEmbeddedStyles() )
00557     {
00558         const FeatureProfile* profile = _source->getFeatureProfile();
00559 
00560         // each feature has its own style, so use that and ignore the style catalog.
00561         osg::ref_ptr<FeatureCursor> cursor = _source->createFeatureCursor( baseQuery );
00562         while( cursor->hasMore() )
00563         {
00564             Feature* feature = cursor->nextFeature();
00565             if ( feature )
00566             {
00567                 FeatureList list;
00568                 list.push_back( feature );
00569                 osg::ref_ptr<FeatureCursor> cursor = new FeatureListCursor(list);
00570 
00571                 FilterContext context( _session.get(), _source->getFeatureProfile(), workingExtent );
00572 
00573                 // note: gridding is not supported for embedded styles.
00574                 osg::ref_ptr<osg::Node> node;
00575 
00576                 // Get the Group that parents all features of this particular style. Note, this
00577                 // might be NULL if the factory does not support style groups.
00578                 osg::Group* styleGroup = _factory->getOrCreateStyleGroup(*feature->style(), _session.get());
00579                 if ( styleGroup )
00580                 {
00581                     if ( !group->containsNode( styleGroup ) )
00582                         group->addChild( styleGroup );
00583                 }
00584 
00585                 if ( _factory->createOrUpdateNode( cursor.get(), *feature->style(), context, node ) )
00586                 {
00587                     if ( node.valid() )
00588                     {
00589                         if ( styleGroup )
00590                             styleGroup->addChild( node.get() );
00591                         else
00592                             group->addChild( node.get() );
00593                     }
00594                 }
00595             }
00596         }
00597     }
00598 
00599     else
00600     {
00601         const StyleSheet* styles = _session->styles();
00602 
00603         // if we have selectors, sort the features into style groups and create a node for each group.
00604         if ( styles->selectors().size() > 0 )
00605         {
00606             for( StyleSelectorList::const_iterator i = styles->selectors().begin(); i != styles->selectors().end(); ++i )
00607             {
00608                 // pull the selected style...
00609                 const StyleSelector& sel = *i;
00610 
00611                 // combine the selection style with the incoming base style:
00612                 Style selectedStyle = *styles->getStyle( sel.getSelectedStyleName() );
00613                 Style combinedStyle = baseStyle.combineWith( selectedStyle );
00614 
00615                 // .. and merge it's query into the existing query
00616                 Query combinedQuery = baseQuery.combineWith( *sel.query() );
00617 
00618                 // then create the node.
00619                 osg::Group* styleGroup = createNodeForStyle( combinedStyle, combinedQuery );
00620                 if ( styleGroup && !group->containsNode(styleGroup) )
00621                     group->addChild( styleGroup );
00622             }
00623         }
00624 
00625         // otherwise, render all the features with a single style
00626         else
00627         {
00628             Style combinedStyle = baseStyle;
00629 
00630             // if there's no base style defined, choose a "default" style from the stylesheet.
00631             if ( baseStyle.empty() )
00632                 combinedStyle = *styles->getDefaultStyle();
00633 
00634             osg::Group* styleGroup = createNodeForStyle( combinedStyle, baseQuery );
00635             if ( styleGroup && !group->containsNode(styleGroup) )
00636                 group->addChild( styleGroup );
00637         }
00638     }
00639 
00640     return group->getNumChildren() > 0 ? group.release() : 0L;
00641 }
00642 
00643 osg::Group*
00644 FeatureModelGraph::createNodeForStyle(const Style& style, const Query& query)
00645 {
00646     osg::Group* styleGroup = 0L;
00647 
00648     // the profile of the features
00649     const FeatureProfile* profile = _source->getFeatureProfile();
00650 
00651     // get the extent of the full set of feature data:
00652     const GeoExtent& extent = profile->getExtent();
00653     
00654     // query the feature source:
00655     osg::ref_ptr<FeatureCursor> cursor = _source->createFeatureCursor( query );
00656 
00657     if ( cursor->hasMore() )
00658     {
00659         Bounds cellBounds =
00660             query.bounds().isSet() ? *query.bounds() : extent.bounds();
00661 
00662         FilterContext context( _session.get(), profile, GeoExtent(profile->getSRS(), cellBounds) );
00663 
00664         // start by culling our feature list to the working extent. By default, this is done by
00665         // checking feature centroids. But the user can override this to crop feature geometry to
00666         // the cell boundaries.
00667         FeatureList workingSet;
00668         cursor->fill( workingSet );
00669 
00670         CropFilter crop( 
00671             _options.levels().isSet() && _options.levels()->cropFeatures() == true ? 
00672             CropFilter::METHOD_CROPPING : CropFilter::METHOD_CENTROID );
00673         context = crop.push( workingSet, context );
00674 
00675         // next, if the usable extent is less than the full extent (i.e. we had to clamp the feature
00676         // extent to fit on the map), calculate the extent of the features in this tile and 
00677         // crop to the map extent if necessary. (Note, if cropFeatures was set to true, this is
00678         // already done)
00679         if ( _featureExtentClamped && _options.levels().isSet() && _options.levels()->cropFeatures() == false )
00680         {
00681             context.extent() = _usableFeatureExtent;
00682             CropFilter crop2( CropFilter::METHOD_CROPPING );
00683             context = crop2.push( workingSet, context );
00684         }
00685 
00686         if ( workingSet.size() > 0 )
00687         {
00688             // next ask the implementation to construct OSG geometry for the cell features.
00689             osg::ref_ptr<osg::Node> node;
00690 
00691             osg::ref_ptr<FeatureCursor> newCursor = new FeatureListCursor(workingSet);
00692 
00693             if ( _factory->createOrUpdateNode( newCursor.get(), style, context, node ) )
00694             {
00695                 if ( !styleGroup )
00696                     styleGroup = _factory->getOrCreateStyleGroup( style, _session.get() );
00697 
00698                 // if it returned a node, add it. (it doesn't necessarily have to)
00699                 if ( node.valid() )
00700                     styleGroup->addChild( node.get() );
00701             }
00702         }
00703 
00704         CacheStats stats = context.resourceCache()->getSkinStats();
00705         OE_DEBUG << LC << "Resource Cache skins: "
00706             << " num=" << stats._entries << ", max=" << stats._maxEntries
00707             << ", queries=" << stats._queries << ", hits=" << (100.0f*stats._hitRatio) << "%"
00708             << std::endl;
00709 
00710     }
00711 
00712 
00713     return styleGroup;
00714 }
00715 
00716 void
00717 FeatureModelGraph::traverse(osg::NodeVisitor& nv)
00718 {
00719     if ( nv.getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR)
00720     {
00721         if (_source->outOfSyncWith(_revision) || _dirty)
00722         {
00723             redraw();
00724         }
00725     }
00726     osg::Group::traverse(nv);
00727 }
00728 
00729 void
00730 FeatureModelGraph::redraw()
00731 {
00732     removeChildren( 0, getNumChildren() );
00733     // if there's a display schema in place, set up for quadtree paging.
00734     if ( _options.levels().isSet() || _useTiledSource ) //_source->getFeatureProfile()->getTiled() )
00735     {
00736         setupPaging();
00737     }
00738     else
00739     {
00740         FeatureLevel defaultLevel( 0.0f, FLT_MAX );
00741         
00742         //Remove all current children        
00743         osg::Node* node = build( defaultLevel, GeoExtent::INVALID, 0 );
00744         if ( node )
00745             addChild( node );
00746     }
00747 
00748     _source->sync( _revision );
00749     _dirty = false;
00750 }
00751 
00752 void
00753 FeatureModelGraph::setStyles( StyleSheet* styles )
00754 {
00755     _session->setStyles( styles );
00756     dirty();
00757 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines