osg::Group*
FeatureModelGraph::build( const FeatureLevel& level, const GeoExtent& extent, const TileKey* key )
{
    // set up for feature indexing if appropriate:
    osg::ref_ptr<osg::Group> group;
    FeatureSourceIndexNode* index = 0L;
    if ( _session->getFeatureSource() && (_options.featureIndexing() == true) )
    {
        index = new FeatureSourceIndexNode( _session->getFeatureSource() );
        group = index;
    }
    else
    {
        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( 
            *_session->getFeatureSource()->getFeatureSourceOptions().name() );

        osg::Node* node = build( style, query, extent, index );
        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, index );
            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 FeatureProfile* featureProfile = _session->getFeatureSource()->getFeatureProfile();
            const GeoExtent& ccExtent = extent.isValid() ? extent : featureProfile->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
                }
            }
        }

        // if indexing is enabled, build the index now.
        if ( index )
            index->reindex();

        return group.release();
    }

    else
    {
        return 0L;
    }
}
Ejemplo n.º 2
0
/**
 * Builds geometry for feature data at a particular level, and constrained by an extent.
 * The extent is either (a) expressed in "extent" literally, as is the case in a non-tiled
 * data source, or (b) expressed implicitly by a TileKey, which is the case for a tiled
 * data source.
 */
osg::Group*
FeatureModelGraph::buildLevel( const FeatureLevel& level, const GeoExtent& extent, const TileKey* key )
{
    // set up for feature indexing if appropriate:
    osg::ref_ptr<osg::Group> group;
    FeatureSourceIndexNode* index = 0L;
    if ( _session->getFeatureSource() && (_options.featureIndexing() == true) )
    {
        index = new FeatureSourceIndexNode( _session->getFeatureSource() );
        group = index;
    }
    else
    {
        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;

    // does the level have a style name set?
    if ( level.styleName().isSet() )
    {
        osg::Node* node = 0L;
        const Style* style = _session->styles()->getStyle( *level.styleName(), false );
        if ( style )
        {
            // found a specific style to use.
            node = createStyleGroup( *style, query, index );
            if ( node )
                group->addChild( node );
        }
        else
        {
            const StyleSelector* selector = _session->styles()->getSelector( *level.styleName() );
            if ( selector )
            {
                buildStyleGroups( selector, query, index, group.get() );
            }
        }
    }

    else
    {
        // attempt to glean the style from the feature source name:
        const Style style = *_session->styles()->getStyle( 
            *_session->getFeatureSource()->getFeatureSourceOptions().name() );

        osg::Node* node = build( style, query, extent, index );
        if ( node )
            group->addChild( node );
    }

    if ( group->getNumChildren() > 0 )
    {
        
        // account for a min-range here. Do not address the max-range here; that happens
        // above when generating paged LOD nodes, etc.        
        float minRange = level.minRange();

        /*
        if ( _options.minRange().isSet() ) 
            minRange = std::max(minRange, *_options.minRange());

        if ( _options.layout().isSet() && _options.layout()->minRange().isSet() )
            minRange = std::max(minRange, *_options.layout()->minRange());
            */

        if ( minRange > 0.0f )
        {
            // minRange can't be less than the tile geometry's radius.
            minRange = std::max(minRange, group->getBound().radius());
            osg::LOD* lod = new osg::LOD();
            lod->addChild( group.get(), minRange, FLT_MAX );
            group = lod;
        }        

        if ( _session->getMapInfo().isGeocentric() && _options.clusterCulling() == true )
        {
            const FeatureProfile* featureProfile = _session->getFeatureSource()->getFeatureProfile();
            const GeoExtent& ccExtent = extent.isValid() ? extent : featureProfile->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 )
                {
                    // 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 = ClusterCullingFactory::create2( group.get(), centerECEF );
                    if ( ccc )
                        group->addCullCallback( ccc );
                }
            }
        }

        // if indexing is enabled, build the index now.
        if ( index )
            index->reindex();

        return group.release();
    }

    else
    {
        return 0L;
    }
}
bool
FeatureQueryTool::handle( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa )
{
    bool handled = false;
    bool attempt;

    if ( _inputPredicate.valid() )
    {
        attempt = _inputPredicate->accept(ea);
    }
    else
    {
        attempt =
            ea.getEventType() == osgGA::GUIEventAdapter::RELEASE &&
            _mouseDown && 
            fabs(ea.getX()-_mouseDownX) <= 3.0 && 
            fabs(ea.getY()-_mouseDownY) <= 3.0;
    }

    if ( attempt )
    {
        osg::View* view = aa.asView();

        Picker picker(
            dynamic_cast<osgViewer::View*>(view),
            _mapNode->getModelLayerGroup() );

        Picker::Hits hits;

        if ( picker.pick( ea.getX(), ea.getY(), hits ) )
        {
            // find the closest indexed feature to the camera. It must be a feature
            // that is not only closest, but exists in the index as well.

            FeatureSourceIndexNode* closestIndex    = 0L;
            FeatureID               closestFID;
            double                  closestDistance = DBL_MAX;
            osg::Vec3d              closestWorldPt;

            for(Picker::Hits::iterator hit = hits.begin(); hit != hits.end(); ++hit )
            {
                FeatureSourceIndexNode* index = picker.getNode<FeatureSourceIndexNode>( *hit );
                if ( index && (hit->distance < closestDistance) )
                {
                    FeatureID fid;
                    if ( index->getFID( hit->drawable, hit->primitiveIndex, fid ) )
                    {
                        closestIndex    = index;
                        closestFID      = fid;
                        closestDistance = hit->distance;
                        closestWorldPt  = hit->matrix.valid() ? hit->localIntersectionPoint * (*hit->matrix.get()) : hit->localIntersectionPoint;
                    }
                }
            }

            if ( closestIndex )
            {
                OE_DEBUG << LC << "HIT: feature ID = " << (unsigned)closestFID << std::endl;

                Callback::EventArgs args;
                args._ea = &ea;
                args._aa = &aa;
                args._worldPoint = closestWorldPt;

                for( Callbacks::iterator i = _callbacks.begin(); i != _callbacks.end(); )
                {
                    if ( i->valid() )
                    {
                        i->get()->onHit( closestIndex, closestFID, args );
                        ++i;
                    }
                    else
                    {
                        i = _callbacks.erase( i );
                    }
                }

                handled = true;
            }
        }

        if ( !handled )
        {
            OE_DEBUG << LC << "miss" << std::endl;

            Callback::EventArgs args;
            args._ea = &ea;
            args._aa = &aa;

            for( Callbacks::iterator i = _callbacks.begin(); i != _callbacks.end(); )
            {
                if ( i->valid() )
                {
                    i->get()->onMiss( args );
                    ++i;
                }
                else
                {
                    i = _callbacks.erase( i );
                }
            }
        }

        _mouseDown = false;
    }

    // unmodified left mouse click
    else if (
        ea.getEventType()  == osgGA::GUIEventAdapter::PUSH &&
        ea.getModKeyMask() == 0 &&
        ea.getButtonMask() == osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON)
    {
        _mouseDown = true;
        _mouseDownX = ea.getX();
        _mouseDownY = ea.getY();
    }

    return handled;
}