osgEarth 2.1.1
|
Public Member Functions | |
FeatureModelGraph (FeatureSource *source, const FeatureModelSourceOptions &options, FeatureNodeFactory *factory, Session *session) | |
osg::Node * | load (unsigned lod, unsigned tileX, unsigned tileY, const std::string &uri) |
StyleSheet * | getStyles () |
void | setStyles (StyleSheet *styles) |
void | dirty () |
virtual void | traverse (osg::NodeVisitor &nv) |
Protected Member Functions | |
virtual | ~FeatureModelGraph () |
void | setupPaging () |
osg::Group * | build (const FeatureLevel &level, const GeoExtent &extent, const TileKey *key) |
osg::Group * | build (const Style &baseStyle, const Query &baseQuery, const GeoExtent &extent) |
Private Member Functions | |
osg::Group * | createNodeForStyle (const Style &style, const Query &query) |
osg::BoundingSphered | getBoundInWorldCoords (const GeoExtent &extent, const MapFrame *mapf) const |
void | buildSubTilePagedLODs (unsigned lod, unsigned tileX, unsigned tileY, const MapFrame *mapFrame, osg::Group *parent) |
void | redraw () |
Private Attributes | |
FeatureModelSourceOptions | _options |
osg::ref_ptr< FeatureSource > | _source |
osg::ref_ptr< FeatureNodeFactory > | _factory |
osg::ref_ptr< Session > | _session |
UID | _uid |
std::set< std::string > | _blacklist |
Threading::ReadWriteMutex | _blacklistMutex |
GeoExtent | _usableFeatureExtent |
bool | _featureExtentClamped |
GeoExtent | _usableMapExtent |
osg::BoundingSphered | _fullWorldBound |
bool | _useTiledSource |
osgEarth::Revision | _revision |
bool | _dirty |
std::vector< const FeatureLevel * > | _lodmap |
A graph that gets its contents from a FeatureNodeFactory. This class will handle all the internals of selecting features, gridding feature data if required, and sorting features based on style. Then for each cell and each style, it will invoke the FeatureNodeFactory to create the actual data for each set.
Definition at line 43 of file FeatureModelGraph.
FeatureModelGraph::FeatureModelGraph | ( | FeatureSource * | source, |
const FeatureModelSourceOptions & | options, | ||
FeatureNodeFactory * | factory, | ||
Session * | session | ||
) |
Constructs a new model graph.
source | Source from which to read feature data |
options | Model source options |
factory | Node factory that will be invoked to compile feature data into nodes |
session | Session under which to create elements in this graph |
Definition at line 151 of file FeatureModelGraph.cpp.
: _source ( source ), _options ( options ), _factory ( factory ), _session ( session ), _dirty ( false ) { _uid = osgEarthFeatureModelPseudoLoader::registerGraph( this ); // install the stylesheet in the session if it doesn't already have one. if ( !session->styles() ) session->setStyles( _options.styles().get() ); // initialize lighting on the graph, if necessary. osg::StateSet* stateSet = getOrCreateStateSet(); if ( _options.enableLighting().isSet() ) stateSet->setMode( GL_LIGHTING, *_options.enableLighting() ? 1 : 0 ); // Calculate the usable extent (in both feature and map coordinates) and bounds. const Profile* mapProfile = session->getMapInfo().getProfile(); // the part of the feature extent that will fit on the map (in map coords): _usableMapExtent = mapProfile->clampAndTransformExtent( _source->getFeatureProfile()->getExtent(), &_featureExtentClamped ); // same, back into feature coords: _usableFeatureExtent = _usableMapExtent.transform( _source->getFeatureProfile()->getSRS() ); // world-space bounds of the feature layer _fullWorldBound = getBoundInWorldCoords( _usableMapExtent, 0L ); // whether to request tiles from the source (if available). if the source is tiled, but the // user manually specified schema levels, don't use the tiles. _useTiledSource = _source->getFeatureProfile()->getTiled(); if ( options.levels().isSet() && options.levels()->getNumLevels() > 0 ) { // the user provided a custom levels setup, so don't use the tiled source (which // provides its own levels setup) _useTiledSource = false; // for each custom level, calculate the best LOD match and store it in the level // layout data. We will use this information later when constructing the SG in // the pager. for( unsigned i = 0; i < options.levels()->getNumLevels(); ++i ) { const FeatureLevel* level = options.levels()->getLevel( i ); unsigned lod = options.levels()->chooseLOD( *level, _fullWorldBound.radius() ); _lodmap.resize( lod+1, 0L ); _lodmap[lod] = level; OE_INFO << LC << source->getName() << ": F.Level max=" << level->maxRange() << ", min=" << level->minRange() << ", LOD=" << lod << std::endl; } } setNumChildrenRequiringUpdateTraversal( 1 ); redraw(); }
FeatureModelGraph::~FeatureModelGraph | ( | ) | [protected, virtual] |
osg::Group * FeatureModelGraph::build | ( | const FeatureLevel & | level, |
const GeoExtent & | extent, | ||
const TileKey * | key | ||
) | [protected] |
Definition at line 462 of file FeatureModelGraph.cpp.
{ osg::ref_ptr<osg::Group> group = new osg::Group(); // form the baseline query, which does a spatial query based on the working extent. Query query; if ( extent.isValid() ) query.bounds() = extent.bounds(); // add a tile key to the query if there is one, to support TFS-style queries if ( key ) query.tileKey() = *key; // now, go through any level-based selectors. const StyleSelectorVector& levelSelectors = level.selectors(); // if there are none, just build once with the default style and query. if ( levelSelectors.size() == 0 ) { // attempt to glean the style from the feature source name: const Style style = *_session->styles()->getStyle( *_source->getFeatureSourceOptions().name() ); osg::Node* node = build( style, query, extent ); if ( node ) group->addChild( node ); } else { for( StyleSelectorVector::const_iterator i = levelSelectors.begin(); i != levelSelectors.end(); ++i ) { const StyleSelector& selector = *i; // fetch the selector's style: const Style* selectorStyle = _session->styles()->getStyle( selector.getSelectedStyleName() ); // combine the selector's query, if it has one: Query selectorQuery = selector.query().isSet() ? query.combineWith( *selector.query() ) : query; osg::Node* node = build( *selectorStyle, selectorQuery, extent ); if ( node ) group->addChild( node ); } } if ( group->getNumChildren() > 0 ) { // account for a min-range here. if ( level.minRange() > 0.0f ) { osg::LOD* lod = new osg::LOD(); lod->addChild( group.get(), level.minRange(), FLT_MAX ); group = lod; } if ( _session->getMapInfo().isGeocentric() && _options.clusterCulling() == true ) { const GeoExtent& ccExtent = extent.isValid() ? extent : _source->getFeatureProfile()->getExtent(); if ( ccExtent.isValid() ) { // if the extent is more than 90 degrees, bail GeoExtent geodeticExtent = ccExtent.transform( ccExtent.getSRS()->getGeographicSRS() ); if ( geodeticExtent.width() < 90.0 && geodeticExtent.height() < 90.0 ) { #if 1 // get the geocentric tile center: osg::Vec3d tileCenter; ccExtent.getCentroid( tileCenter.x(), tileCenter.y() ); osg::Vec3d centerECEF; ccExtent.getSRS()->transformToECEF( tileCenter, centerECEF ); osg::NodeCallback* ccc = ClusterCullerFactory::create( group.get(), centerECEF ); if ( ccc ) group->addCullCallback( ccc ); #endif } } } return group.release(); } else { return 0L; } }
osg::Group * FeatureModelGraph::build | ( | const Style & | baseStyle, |
const Query & | baseQuery, | ||
const GeoExtent & | extent | ||
) | [protected] |
Definition at line 552 of file FeatureModelGraph.cpp.
{ osg::ref_ptr<osg::Group> group = new osg::Group(); if ( _source->hasEmbeddedStyles() ) { const FeatureProfile* profile = _source->getFeatureProfile(); // each feature has its own style, so use that and ignore the style catalog. osg::ref_ptr<FeatureCursor> cursor = _source->createFeatureCursor( baseQuery ); while( cursor->hasMore() ) { Feature* feature = cursor->nextFeature(); if ( feature ) { FeatureList list; list.push_back( feature ); osg::ref_ptr<FeatureCursor> cursor = new FeatureListCursor(list); FilterContext context( _session.get(), _source->getFeatureProfile(), workingExtent ); // note: gridding is not supported for embedded styles. osg::ref_ptr<osg::Node> node; // Get the Group that parents all features of this particular style. Note, this // might be NULL if the factory does not support style groups. osg::Group* styleGroup = _factory->getOrCreateStyleGroup(*feature->style(), _session.get()); if ( styleGroup ) { if ( !group->containsNode( styleGroup ) ) group->addChild( styleGroup ); } if ( _factory->createOrUpdateNode( cursor.get(), *feature->style(), context, node ) ) { if ( node.valid() ) { if ( styleGroup ) styleGroup->addChild( node.get() ); else group->addChild( node.get() ); } } } } } else { const StyleSheet* styles = _session->styles(); // if we have selectors, sort the features into style groups and create a node for each group. if ( styles->selectors().size() > 0 ) { for( StyleSelectorList::const_iterator i = styles->selectors().begin(); i != styles->selectors().end(); ++i ) { // pull the selected style... const StyleSelector& sel = *i; // combine the selection style with the incoming base style: Style selectedStyle = *styles->getStyle( sel.getSelectedStyleName() ); Style combinedStyle = baseStyle.combineWith( selectedStyle ); // .. and merge it's query into the existing query Query combinedQuery = baseQuery.combineWith( *sel.query() ); // then create the node. osg::Group* styleGroup = createNodeForStyle( combinedStyle, combinedQuery ); if ( styleGroup && !group->containsNode(styleGroup) ) group->addChild( styleGroup ); } } // otherwise, render all the features with a single style else { Style combinedStyle = baseStyle; // if there's no base style defined, choose a "default" style from the stylesheet. if ( baseStyle.empty() ) combinedStyle = *styles->getDefaultStyle(); osg::Group* styleGroup = createNodeForStyle( combinedStyle, baseQuery ); if ( styleGroup && !group->containsNode(styleGroup) ) group->addChild( styleGroup ); } } return group->getNumChildren() > 0 ? group.release() : 0L; }
void FeatureModelGraph::buildSubTilePagedLODs | ( | unsigned | lod, |
unsigned | tileX, | ||
unsigned | tileY, | ||
const MapFrame * | mapFrame, | ||
osg::Group * | parent | ||
) | [private] |
Definition at line 412 of file FeatureModelGraph.cpp.
{ unsigned subtileLOD = parentLOD + 1; unsigned subtileX = parentTileX * 2; unsigned subtileY = parentTileY * 2; // make a paged LOD for each subtile: for( unsigned u = subtileX; u <= subtileX + 1; ++u ) { for( unsigned v = subtileY; v <= subtileY + 1; ++v ) { GeoExtent subtileFeatureExtent = s_getTileExtent( subtileLOD, u, v, _usableFeatureExtent ); osg::BoundingSphered subtile_bs = getBoundInWorldCoords( subtileFeatureExtent, mapf ); // Camera range for the PLODs. This should always be sufficient because // the max range of a FeatureLevel below this will, by definition, have a max range // less than or equal to this number -- based on how the LODs were chosen in // setupPaging. float maxRange = subtile_bs.radius() * _options.levels()->tileSizeFactor().value(); std::string uri = s_makeURI( _uid, subtileLOD, u, v ); // check the blacklist to make sure we haven't unsuccessfully tried // this URI before bool blacklisted = false; { Threading::ScopedReadLock sharedLock( _blacklistMutex ); blacklisted = _blacklist.find( uri ) != _blacklist.end(); } if ( !blacklisted ) { OE_DEBUG << LC << " " << uri << std::fixed << "; center = " << subtile_bs.center().x() << "," << subtile_bs.center().y() << "," << subtile_bs.center().z() << "; radius = " << subtile_bs.radius() << std::endl; osg::Group* pagedNode = createPagedNode( subtile_bs, uri, 0.0f, maxRange ); parent->addChild( pagedNode ); } } } }
osg::Group * FeatureModelGraph::createNodeForStyle | ( | const Style & | style, |
const Query & | query | ||
) | [private] |
Definition at line 644 of file FeatureModelGraph.cpp.
{ osg::Group* styleGroup = 0L; // the profile of the features const FeatureProfile* profile = _source->getFeatureProfile(); // get the extent of the full set of feature data: const GeoExtent& extent = profile->getExtent(); // query the feature source: osg::ref_ptr<FeatureCursor> cursor = _source->createFeatureCursor( query ); if ( cursor->hasMore() ) { Bounds cellBounds = query.bounds().isSet() ? *query.bounds() : extent.bounds(); FilterContext context( _session.get(), profile, GeoExtent(profile->getSRS(), cellBounds) ); // start by culling our feature list to the working extent. By default, this is done by // checking feature centroids. But the user can override this to crop feature geometry to // the cell boundaries. FeatureList workingSet; cursor->fill( workingSet ); CropFilter crop( _options.levels().isSet() && _options.levels()->cropFeatures() == true ? CropFilter::METHOD_CROPPING : CropFilter::METHOD_CENTROID ); context = crop.push( workingSet, context ); // next, if the usable extent is less than the full extent (i.e. we had to clamp the feature // extent to fit on the map), calculate the extent of the features in this tile and // crop to the map extent if necessary. (Note, if cropFeatures was set to true, this is // already done) if ( _featureExtentClamped && _options.levels().isSet() && _options.levels()->cropFeatures() == false ) { context.extent() = _usableFeatureExtent; CropFilter crop2( CropFilter::METHOD_CROPPING ); context = crop2.push( workingSet, context ); } if ( workingSet.size() > 0 ) { // next ask the implementation to construct OSG geometry for the cell features. osg::ref_ptr<osg::Node> node; osg::ref_ptr<FeatureCursor> newCursor = new FeatureListCursor(workingSet); if ( _factory->createOrUpdateNode( newCursor.get(), style, context, node ) ) { if ( !styleGroup ) styleGroup = _factory->getOrCreateStyleGroup( style, _session.get() ); // if it returned a node, add it. (it doesn't necessarily have to) if ( node.valid() ) styleGroup->addChild( node.get() ); } } CacheStats stats = context.resourceCache()->getSkinStats(); OE_DEBUG << LC << "Resource Cache skins: " << " num=" << stats._entries << ", max=" << stats._maxEntries << ", queries=" << stats._queries << ", hits=" << (100.0f*stats._hitRatio) << "%" << std::endl; } return styleGroup; }
void FeatureModelGraph::dirty | ( | ) |
Definition at line 224 of file FeatureModelGraph.cpp.
{ _dirty = true; }
osg::BoundingSphered FeatureModelGraph::getBoundInWorldCoords | ( | const GeoExtent & | extent, |
const MapFrame * | mapf | ||
) | const [private] |
Definition at line 230 of file FeatureModelGraph.cpp.
{ osg::Vec3d center, corner; double z = 0.0; GeoExtent workingExtent; if ( extent.getSRS()->isEquivalentTo( _usableMapExtent.getSRS() ) ) { workingExtent = extent; } else { workingExtent = extent.transform( _usableMapExtent.getSRS() ); // safe. } workingExtent.getCentroid( center.x(), center.y() ); if ( mapf ) { // note: use the lowest possible resolution to speed up queries ElevationQuery query( *mapf ); query.getElevation( center, mapf->getProfile()->getSRS(), center.z(), DBL_MAX ); } corner.x() = workingExtent.xMin(); corner.y() = workingExtent.yMin(); corner.z() = z; if ( _session->getMapInfo().isGeocentric() ) { workingExtent.getSRS()->transformToECEF( center, center ); workingExtent.getSRS()->transformToECEF( corner, corner ); } return osg::BoundingSphered( center, (center-corner).length() ); }
StyleSheet* osgEarth::Features::FeatureModelGraph::getStyles | ( | ) | [inline] |
Definition at line 69 of file FeatureModelGraph.
{ return _session->styles(); }
osg::Node * FeatureModelGraph::load | ( | unsigned | lod, |
unsigned | tileX, | ||
unsigned | tileY, | ||
const std::string & | uri | ||
) |
Loads and returns a subnode. Used internally for paging.
Definition at line 287 of file FeatureModelGraph.cpp.
{ OE_DEBUG << LC << "load: " << lod << "_" << tileX << "_" << tileY << std::endl; osg::Group* result = 0L; if ( _useTiledSource ) { // A "tiled" source has a pre-generted tile hierarchy, but no range information. // We will be calculating the LOD ranges here. // The extent of this tile: GeoExtent tileExtent = s_getTileExtent( lod, tileX, tileY, _usableFeatureExtent ); // Calculate the bounds of this new tile: MapFrame mapf = _session->createMapFrame(); osg::BoundingSphered tileBound = getBoundInWorldCoords( tileExtent, &mapf ); // Apply the tile range multiplier to calculate a max camera range. The max range is // the geographic radius of the tile times the multiplier. float tileFactor = _options.levels().isSet() ? _options.levels()->tileSizeFactor().get() : 15.0f; double maxRange = tileBound.radius() * tileFactor; FeatureLevel level( 0, maxRange ); // Construct a tile key that will be used to query the source for this tile. TileKey key(lod, tileX, tileY, _source->getFeatureProfile()->getProfile()); osg::Group* geometry = build( level, tileExtent, &key ); result = geometry; if (lod < _source->getFeatureProfile()->getMaxLevel()) { // see if there are any more levels. If so, build some pagedlods to bring the // next level in. FeatureLevel nextLevel(0, maxRange/2.0); osg::ref_ptr<osg::Group> group = new osg::Group(); // calculate the LOD of the next level: if ( lod+1 != ~0 ) { MapFrame mapf = _session->createMapFrame(); buildSubTilePagedLODs( lod, tileX, tileY, &mapf, group.get() ); // slap the geometry in there afterwards, if there is any if ( geometry ) group->addChild( geometry ); result = group.release(); } } } else if ( !_options.levels().isSet() || _options.levels()->getNumLevels() == 0 ) { // This is a non-tiled data source that has NO level details. In this case, // we simply want to load all features at once and make them visible at // maximum camera range. FeatureLevel all( 0.0f, FLT_MAX ); result = build( all, GeoExtent::INVALID, 0 ); } else if ( lod < _lodmap.size() ) { // This path computes the SG for a model graph with explicity-defined levels of // detail. We already calculated the LOD level map in setupPaging(). If the // current LOD points to an actual FeatureLevel, we build the geometry for that // level in the tile. osg::Group* geometry = 0L; const FeatureLevel* level = _lodmap[lod]; if ( level ) { // There exists a real data level at this LOD. So build the geometry that will // represent this tile. GeoExtent tileExtent = lod > 0 ? s_getTileExtent( lod, tileX, tileY, _usableFeatureExtent ) : _usableFeatureExtent; geometry = build( *level, tileExtent, 0 ); result = geometry; } if ( lod < _lodmap.size()-1 ) { // There are more populated levels below this one. So build the subtile // PagedLODs that will load them. osg::ref_ptr<osg::Group> group = new osg::Group(); MapFrame mapf = _session->createMapFrame(); buildSubTilePagedLODs( lod, tileX, tileY, &mapf, group.get() ); if ( geometry ) group->addChild( geometry ); result = group.release(); } } if ( !result ) { // If the read resulting in nothing, create an empty group so that the read // (technically) succeeds and the pager won't try to load the null child // over and over. result = new osg::Group(); } else { RemoveEmptyGroupsVisitor::run( result ); } if ( result->getNumChildren() == 0 ) { // if the result group contains no data, blacklist it so we never try to load it again. Threading::ScopedWriteLock exclusiveLock( _blacklistMutex ); _blacklist.insert( uri ); OE_DEBUG << LC << "Blacklisting: " << uri << std::endl; } return result; }
void FeatureModelGraph::redraw | ( | ) | [private] |
Definition at line 730 of file FeatureModelGraph.cpp.
{ removeChildren( 0, getNumChildren() ); // if there's a display schema in place, set up for quadtree paging. if ( _options.levels().isSet() || _useTiledSource ) //_source->getFeatureProfile()->getTiled() ) { setupPaging(); } else { FeatureLevel defaultLevel( 0.0f, FLT_MAX ); //Remove all current children osg::Node* node = build( defaultLevel, GeoExtent::INVALID, 0 ); if ( node ) addChild( node ); } _source->sync( _revision ); _dirty = false; }
void FeatureModelGraph::setStyles | ( | StyleSheet * | styles | ) |
void FeatureModelGraph::setupPaging | ( | ) | [protected] |
Definition at line 269 of file FeatureModelGraph.cpp.
{ // calculate the bounds of the full data extent: MapFrame mapf = _session->createMapFrame(); osg::BoundingSphered bs = getBoundInWorldCoords( _usableMapExtent, &mapf ); // calculate the max range for the top-level PLOD: float maxRange = bs.radius() * _options.levels()->tileSizeFactor().value(); // build the URI for the top-level paged LOD: std::string uri = s_makeURI( _uid, 0, 0, 0 ); // bulid the top level Paged LOD: osg::Group* pagedNode = createPagedNode( bs, uri, 0.0f, maxRange ); this->addChild( pagedNode ); }
void FeatureModelGraph::traverse | ( | osg::NodeVisitor & | nv | ) | [virtual] |
Definition at line 717 of file FeatureModelGraph.cpp.
{ if ( nv.getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR) { if (_source->outOfSyncWith(_revision) || _dirty) { redraw(); } } osg::Group::traverse(nv); }
std::set<std::string> osgEarth::Features::FeatureModelGraph::_blacklist [private] |
Definition at line 105 of file FeatureModelGraph.
Definition at line 106 of file FeatureModelGraph.
bool osgEarth::Features::FeatureModelGraph::_dirty [private] |
Definition at line 113 of file FeatureModelGraph.
osg::ref_ptr<FeatureNodeFactory> osgEarth::Features::FeatureModelGraph::_factory [private] |
Definition at line 102 of file FeatureModelGraph.
bool osgEarth::Features::FeatureModelGraph::_featureExtentClamped [private] |
Definition at line 108 of file FeatureModelGraph.
osg::BoundingSphered osgEarth::Features::FeatureModelGraph::_fullWorldBound [private] |
Definition at line 110 of file FeatureModelGraph.
std::vector<const FeatureLevel*> osgEarth::Features::FeatureModelGraph::_lodmap [private] |
Definition at line 114 of file FeatureModelGraph.
Definition at line 100 of file FeatureModelGraph.
Definition at line 112 of file FeatureModelGraph.
osg::ref_ptr<Session> osgEarth::Features::FeatureModelGraph::_session [private] |
Definition at line 103 of file FeatureModelGraph.
osg::ref_ptr<FeatureSource> osgEarth::Features::FeatureModelGraph::_source [private] |
Definition at line 101 of file FeatureModelGraph.
Definition at line 104 of file FeatureModelGraph.
Definition at line 107 of file FeatureModelGraph.
Definition at line 109 of file FeatureModelGraph.
bool osgEarth::Features::FeatureModelGraph::_useTiledSource [private] |
Definition at line 111 of file FeatureModelGraph.