osg::Geode*
BuildGeometryFilter::processPoints(FeatureList& features, FilterContext& context)
{
    osg::Geode* geode = new osg::Geode();

    bool makeECEF = false;
    const SpatialReference* featureSRS = 0L;
    const SpatialReference* mapSRS = 0L;

    // set up referencing information:
    if ( context.isGeoreferenced() )
    {
        makeECEF   = context.getSession()->getMapInfo().isGeocentric();
        featureSRS = context.extent()->getSRS();
        mapSRS     = context.getSession()->getMapInfo().getProfile()->getSRS();
    }

    for( FeatureList::iterator f = features.begin(); f != features.end(); ++f )
    {
        Feature* input = f->get();

        GeometryIterator parts( input->getGeometry(), true );
        while( parts.hasMore() )
        {
            Geometry* part = parts.next();

            // extract the required point symbol; bail out if not found.
            const PointSymbol* point =
                input->style().isSet() && input->style()->has<PointSymbol>() ? input->style()->get<PointSymbol>() :
                _style.get<PointSymbol>();

            if ( !point )
                continue;

            // resolve the color:
            osg::Vec4f primaryColor = point->fill()->color();
            
            osg::ref_ptr<osg::Geometry> osgGeom = new osg::Geometry();
            //osgGeom->setUseVertexBufferObjects( true );
            //osgGeom->setUseDisplayList( false );

            // embed the feature name if requested. Warning: blocks geometry merge optimization!
            if ( _featureNameExpr.isSet() )
            {
                const std::string& name = input->eval( _featureNameExpr.mutable_value(), &context );
                osgGeom->setName( name );
            }

            // build the geometry:
            osg::Vec3Array* allPoints = new osg::Vec3Array();

            transformAndLocalize( part->asVector(), featureSRS, allPoints, mapSRS, _world2local, makeECEF );

            osgGeom->addPrimitiveSet( new osg::DrawArrays(GL_POINTS, 0, allPoints->getNumElements()) );
            osgGeom->setVertexArray( allPoints );

            if ( input->style().isSet() )
            {
                //TODO: re-evaluate this. does it hinder geometry merging?
                applyPointSymbology( osgGeom->getOrCreateStateSet(), point );
            }

            // assign the primary color (PER_VERTEX required for later optimization)
            osg::Vec4Array* colors = new osg::Vec4Array;
            colors->assign( osgGeom->getVertexArray()->getNumElements(), primaryColor );
            osgGeom->setColorArray( colors );
            osgGeom->setColorBinding( osg::Geometry::BIND_PER_VERTEX );

            geode->addDrawable( osgGeom );

            // record the geometry's primitive set(s) in the index:
            if ( context.featureIndex() )
                context.featureIndex()->tagDrawable( osgGeom, input );
        
            // install clamping attributes if necessary
            if (_style.has<AltitudeSymbol>() &&
                _style.get<AltitudeSymbol>()->technique() == AltitudeSymbol::TECHNIQUE_GPU)
            {            
                Clamping::applyDefaultClampingAttrs( osgGeom, input->getDouble("__oe_verticalOffset", 0.0) );
            }
        }
    }
    
    return geode;
}
Example #2
0
osg::Node*
PolygonizeLinesFilter::push(FeatureList& input, FilterContext& cx)
{
    // compute the coordinate localization matrices.
    computeLocalizers( cx );

    // establish some things
    bool                    makeECEF   = false;
    const SpatialReference* featureSRS = 0L;
    const SpatialReference* mapSRS     = 0L;

    if ( cx.isGeoreferenced() )
    {
        makeECEF   = cx.getSession()->getMapInfo().isGeocentric();
        featureSRS = cx.extent()->getSRS();
        mapSRS     = cx.getSession()->getMapInfo().getProfile()->getSRS();
    }

    // The operator we'll use to make lines into polygons.
    const LineSymbol* line = _style.get<LineSymbol>();
    PolygonizeLinesOperator polygonize( line ? (*line->stroke()) : Stroke() );

    // Geode to hold all the geometries.
    osg::Geode* geode = new osg::Geode();

    // iterate over all features.
    for( FeatureList::iterator i = input.begin(); i != input.end(); ++i )
    {
        Feature* f = i->get();

        // iterate over all the feature's geometry parts. We will treat
        // them as lines strings.
        GeometryIterator parts( f->getGeometry(), false );
        while( parts.hasMore() )
        {
            Geometry* part = parts.next();

            // skip empty geometry
            if ( part->size() == 0 )
                continue;

            // transform the geometry into the target SRS and localize it about 
            // a local reference point.
            osg::Vec3Array* verts   = new osg::Vec3Array();
            osg::Vec3Array* normals = new osg::Vec3Array();
            transformAndLocalize( part->asVector(), featureSRS, verts, normals, mapSRS, _world2local, makeECEF );

            // turn the lines into polygons.
            osg::Geometry* geom = polygonize( verts, normals );
            geode->addDrawable( geom );

            // record the geometry's primitive set(s) in the index:
            if ( cx.featureIndex() )
                cx.featureIndex()->tagPrimitiveSets( geom, f );
        }
    }

    // attempt to combine geometries for better performance
    MeshConsolidator::run( *geode );

    // GPU performance optimization:
    VertexCacheOptimizer vco;
    geode->accept( vco );

    // If we're auto-scaling, we need a shader
    float minPixels = line ? line->stroke()->minPixels().getOrUse( 0.0f ) : 0.0f;
    if ( minPixels > 0.0f )
    {
        osg::StateSet* stateSet = geode->getOrCreateStateSet();

        VirtualProgram* vp = VirtualProgram::getOrCreate(stateSet);
        vp->setName( "osgEarth::PolygonizeLines" );

        const char* vs =
            "#version " GLSL_VERSION_STR "\n"
            GLSL_DEFAULT_PRECISION_FLOAT "\n"
            "attribute vec3   oe_polyline_center; \n"
            "uniform   float  oe_polyline_scale;  \n"
            "uniform   float  oe_polyline_min_pixels; \n"
            "uniform   mat3   oe_WindowScaleMatrix; \n"

            "void oe_polyline_scalelines(inout vec4 VertexMODEL) \n"
            "{ \n"
            "   if ( oe_polyline_scale != 1.0 || oe_polyline_min_pixels > 0.0 ) \n"
            "   { \n"
            "       vec4  center_model = vec4(oe_polyline_center*VertexMODEL.w, VertexMODEL.w); \n"
            "       vec4  vector_model = VertexMODEL - center_model; \n"
            "       if ( length(vector_model.xyz) > 0.0 ) \n"
            "       { \n"
            "           float scale = oe_polyline_scale; \n"

            "           vec4 vertex_clip = gl_ModelViewProjectionMatrix * VertexMODEL; \n"
            "           vec4 center_clip = gl_ModelViewProjectionMatrix * center_model; \n"
            "           vec4 vector_clip = vertex_clip - center_clip; \n"

            "           if ( oe_polyline_min_pixels > 0.0 ) \n"
            "           { \n"
            "               vec3 vector_win = oe_WindowScaleMatrix * (vertex_clip.xyz/vertex_clip.w - center_clip.xyz/center_clip.w); \n"
            "               float min_scale = max( (0.5*oe_polyline_min_pixels)/length(vector_win.xy), 1.0 ); \n"
            "               scale = max( scale, min_scale ); \n"
            "           } \n"

            "           VertexMODEL = center_model + vector_model*scale; \n"
            "        } \n"
            "    } \n"
            "} \n";

        vp->setFunction( "oe_polyline_scalelines", vs, ShaderComp::LOCATION_VERTEX_MODEL );
        vp->addBindAttribLocation( "oe_polyline_center", osg::Drawable::ATTRIBUTE_6 );

        // add the default scaling uniform.
        // good way to test:
        //    osgearth_viewer earthfile --uniform oe_polyline_scale 1.0 10.0
        osg::Uniform* scaleU = new osg::Uniform(osg::Uniform::FLOAT, "oe_polyline_scale");
        scaleU->set( 1.0f );
        stateSet->addUniform( scaleU, 1 );

        // the default "min pixels" uniform.
        osg::Uniform* minPixelsU = new osg::Uniform(osg::Uniform::FLOAT, "oe_polyline_min_pixels");
        minPixelsU->set( minPixels );
        stateSet->addUniform( minPixelsU, 1 );
    }

    return delocalize( geode );
}
Example #3
0
FilterContext
BuildGeometryFilter::push( FeatureList& input, const FilterContext& context )
{
    reset();

    OE_DEBUG << LC 
        << context.toString() << std::endl;

    bool ok = true;
    for( FeatureList::iterator i = input.begin(); i != input.end(); i++ )
        if ( !push( i->get(), context ) )
            ok = false;

    // In a feature class with one point-per-feature, you end up with one geometry per point,
    // which results is (a) very bad performance and (b) geometries with a zero bbox that therefore
    // don't draw. This is not a total solution (won't work for a single point, isn't friendly for
    // doing feature-selection, etc.) but is a workable temporary fix. In the future we're going
    // to replace this filter anyway with something more highly optimized (a la osgGIS).
    //
    // however...seems that MERGE_GEOMETRY destroys almost everything except for points!!
    if ( _mergeGeometry == true )
    {
        osgUtil::Optimizer optimizer;
        optimizer.optimize( _geode.get(), osgUtil::Optimizer::MERGE_GEOMETRY );
    }

    if ( ok )
    {
        if ( !_style.empty() && _geode.valid() )
        {
            // could optimize this to only happen is lines or points were created ..
            const LineSymbol* lineSymbol = _style.getSymbol<LineSymbol>();
            float size = 1.0;
            if (lineSymbol)
                size = lineSymbol->stroke()->width().value();

            _geode->getOrCreateStateSet()->setAttribute( new osg::Point(size), osg::StateAttribute::ON );
            _geode->getOrCreateStateSet()->setAttribute( new osg::LineWidth(size), osg::StateAttribute::ON );

            const PointSymbol* pointSymbol = _style.getSymbol<PointSymbol>();
            if ( pointSymbol && pointSymbol->size().isSet() )
                _geode->getOrCreateStateSet()->setAttribute( 
                    new osg::Point( *pointSymbol->size() ), osg::StateAttribute::ON );
        }

        _result = _geode.release();

        if ( context.hasReferenceFrame() )
        {
            osg::MatrixTransform* delocalizer = new osg::MatrixTransform( context.inverseReferenceFrame() );
            delocalizer->addChild( _result.get() );
            _result = delocalizer;
        }
    }
    else
    {
        _result = 0L;
    }

    FilterContext outCx( context );
    outCx.setReferenceFrame( osg::Matrixd::identity() ); // clear the ref frame.
    return outCx;
}
osg::Geode*
BuildGeometryFilter::processLines(FeatureList& features, FilterContext& context)
{
    osg::Geode* geode = new osg::Geode();

    bool makeECEF = false;
    const SpatialReference* featureSRS = 0L;
    const SpatialReference* mapSRS = 0L;

    // set up referencing information:
    if ( context.isGeoreferenced() )
    {
        makeECEF   = context.getSession()->getMapInfo().isGeocentric();
        featureSRS = context.extent()->getSRS();
        mapSRS     = context.getSession()->getMapInfo().getProfile()->getSRS();
    }

    for( FeatureList::iterator f = features.begin(); f != features.end(); ++f )
    {
        Feature* input = f->get();

        // extract the required line symbol; bail out if not found.
        const LineSymbol* line = 
            input->style().isSet() && input->style()->has<LineSymbol>() ? input->style()->get<LineSymbol>() :
            _style.get<LineSymbol>();

        if ( !line )
            continue;

        // run a symbol script if present.
        if ( line->script().isSet() )
        {
            StringExpression temp( line->script().get() );
            input->eval( temp, &context );
        }

        GeometryIterator parts( input->getGeometry(), true );
        while( parts.hasMore() )
        {
            Geometry* part = parts.next();

            // skip invalid geometry for lines.
            if ( part->size() < 2 )
                continue;

            // if the underlying geometry is a ring (or a polygon), use a line loop; otherwise
            // use a line strip.
            GLenum primMode = dynamic_cast<Ring*>(part) ? GL_LINE_LOOP : GL_LINE_STRIP;

            // resolve the color:
            osg::Vec4f primaryColor = line->stroke()->color();
            
            osg::ref_ptr<osg::Geometry> osgGeom = new osg::Geometry();
            //osgGeom->setUseVertexBufferObjects( true );
            //osgGeom->setUseDisplayList( false );

            // embed the feature name if requested. Warning: blocks geometry merge optimization!
            if ( _featureNameExpr.isSet() )
            {
                const std::string& name = input->eval( _featureNameExpr.mutable_value(), &context );
                osgGeom->setName( name );
            }

            // build the geometry:
            osg::Vec3Array* allPoints = new osg::Vec3Array();

            transformAndLocalize( part->asVector(), featureSRS, allPoints, mapSRS, _world2local, makeECEF );

            osgGeom->addPrimitiveSet( new osg::DrawArrays(primMode, 0, allPoints->getNumElements()) );
            osgGeom->setVertexArray( allPoints );

            if ( input->style().isSet() )
            {
                //TODO: re-evaluate this. does it hinder geometry merging?
                applyLineSymbology( osgGeom->getOrCreateStateSet(), line );
            }
            
            // subdivide the mesh if necessary to conform to an ECEF globe;
            // but if the tessellation is set to zero, or if the style specifies a
            // tessellation size, skip this step.
            if ( makeECEF && !line->tessellation().isSetTo(0) && !line->tessellationSize().isSet() )
            {
                double threshold = osg::DegreesToRadians( *_maxAngle_deg );
                OE_DEBUG << "Running mesh subdivider with threshold " << *_maxAngle_deg << std::endl;

                MeshSubdivider ms( _world2local, _local2world );
                //ms.setMaxElementsPerEBO( INT_MAX );
                if ( input->geoInterp().isSet() )
                    ms.run( *osgGeom, threshold, *input->geoInterp() );
                else
                    ms.run( *osgGeom, threshold, *_geoInterp );
            }

            // assign the primary color (PER_VERTEX required for later optimization)
            osg::Vec4Array* colors = new osg::Vec4Array;
            colors->assign( osgGeom->getVertexArray()->getNumElements(), primaryColor );
            osgGeom->setColorArray( colors );
            osgGeom->setColorBinding( osg::Geometry::BIND_PER_VERTEX );

            geode->addDrawable( osgGeom );

            // record the geometry's primitive set(s) in the index:
            if ( context.featureIndex() )
                context.featureIndex()->tagDrawable( osgGeom, input );
        
            // install clamping attributes if necessary
            if (_style.has<AltitudeSymbol>() &&
                _style.get<AltitudeSymbol>()->technique() == AltitudeSymbol::TECHNIQUE_GPU)
            {
                Clamping::applyDefaultClampingAttrs( osgGeom, input->getDouble("__oe_verticalOffset", 0.0) );
            }
        }
    }
    
    return geode;
}
Example #5
0
void
AltitudeFilter::pushAndDontClamp( FeatureList& features, FilterContext& cx )
{
    NumericExpression scaleExpr;
    if ( _altitude.valid() && _altitude->verticalScale().isSet() )
        scaleExpr = *_altitude->verticalScale();

    NumericExpression offsetExpr;
    if ( _altitude.valid() && _altitude->verticalOffset().isSet() )
        offsetExpr = *_altitude->verticalOffset();

    bool gpuClamping =
        _altitude.valid() &&
        _altitude->technique() == _altitude->TECHNIQUE_GPU;

    for( FeatureList::iterator i = features.begin(); i != features.end(); ++i )
    {
        Feature* feature = i->get();
        
        // run a symbol script if present.
        if ( _altitude.valid() && _altitude->script().isSet() )
        {
            StringExpression temp( _altitude->script().get() );
            feature->eval( temp, &cx );
        }

        double minHAT       =  DBL_MAX;
        double maxHAT       = -DBL_MAX;

        double scaleZ = 1.0;
        if ( _altitude.valid() && _altitude->verticalScale().isSet() )
            scaleZ = feature->eval( scaleExpr, &cx );

        double offsetZ = 0.0;
        if ( _altitude.valid() && _altitude->verticalOffset().isSet() )
            offsetZ = feature->eval( offsetExpr, &cx );
        
        GeometryIterator gi( feature->getGeometry() );
        while( gi.hasMore() )
        {
            Geometry* geom = gi.next();
            for( Geometry::iterator g = geom->begin(); g != geom->end(); ++g )
            {
                if ( !gpuClamping )
                {
                    g->z() *= scaleZ;
                    g->z() += offsetZ;
                }

                if ( g->z() < minHAT )
                    minHAT = g->z();
                if ( g->z() > maxHAT )
                    maxHAT = g->z();
            }
        }

        if ( minHAT != DBL_MAX )
        {
            feature->set( "__min_hat", minHAT );
            feature->set( "__max_hat", maxHAT );
        }

        // encode the Z offset if
        if ( gpuClamping )
        {
            feature->set("__oe_verticalScale",  scaleZ);
            feature->set("__oe_verticalOffset", offsetZ);
        }
    }
}
Example #6
0
bool
BuildGeometryFilter::process( FeatureList& features, const FilterContext& context )
{
    bool makeECEF = false;
    const SpatialReference* featureSRS = 0L;
    const SpatialReference* mapSRS = 0L;

    if ( context.isGeoreferenced() )
    {
        makeECEF   = context.getSession()->getMapInfo().isGeocentric();
        featureSRS = context.extent()->getSRS();
        mapSRS     = context.getSession()->getMapInfo().getProfile()->getSRS();
    }

    for( FeatureList::iterator f = features.begin(); f != features.end(); ++f )
    {
        Feature* input = f->get();

        GeometryIterator parts( input->getGeometry(), false );
        while( parts.hasMore() )
        {
            Geometry* part = parts.next();

            // skip empty geometry
            if ( part->size() == 0 )
                continue;

            const Style& myStyle = input->style().isSet() ? *input->style() : _style;

            bool  setLinePropsHere   = input->style().isSet(); // otherwise it will be set globally, we assume
            float width              = 1.0f;
            bool  hasPolyOutline     = false;

            const PointSymbol*   pointSymbol = myStyle.get<PointSymbol>();
            const LineSymbol*    lineSymbol  = myStyle.get<LineSymbol>();
            const PolygonSymbol* polySymbol  = myStyle.get<PolygonSymbol>();

            // resolve the geometry type from the component type and the symbology:
            Geometry::Type renderType = Geometry::TYPE_UNKNOWN;

            // First priority is a matching part type and symbol:
            if ( polySymbol != 0L && part->getType() == Geometry::TYPE_POLYGON )
            {
                renderType = Geometry::TYPE_POLYGON;
            }
            else if ( lineSymbol != 0L && part->isLinear() )
            {
                renderType = part->getType();
            }
            else if ( pointSymbol != 0L && part->getType() == Geometry::TYPE_POINTSET )
            {
                renderType = Geometry::TYPE_POINTSET;
            }

            // Second priority is the symbol:
            else if ( polySymbol != 0L )
            {
                renderType = Geometry::TYPE_POLYGON;
            }
            else if ( lineSymbol != 0L )
            {
                if ( part->getType() == Geometry::TYPE_POLYGON )
                    renderType = Geometry::TYPE_RING;
                else
                    renderType = Geometry::TYPE_LINESTRING;
            }
            else if ( pointSymbol != 0L )
            {
                renderType = Geometry::TYPE_POINTSET;
            }

            // No symbol? just use the geometry type.
            else
            {
                renderType = part->getType();
            }

            // validate the geometry:
            if ( renderType == Geometry::TYPE_POLYGON && part->size() < 3 )
                continue;
            else if ( (renderType == Geometry::TYPE_LINESTRING || renderType == Geometry::TYPE_RING) && part->size() < 2 )
                continue;

            // resolve the color:
            osg::Vec4f primaryColor =
                polySymbol ? osg::Vec4f(polySymbol->fill()->color()) :
                lineSymbol ? osg::Vec4f(lineSymbol->stroke()->color()) :
                pointSymbol ? osg::Vec4f(pointSymbol->fill()->color()) :
                osg::Vec4f(1,1,1,1);
            
            osg::Geometry* osgGeom = new osg::Geometry();
            osgGeom->setUseVertexBufferObjects( _useVertexBufferObjects.value() );

            if ( _featureNameExpr.isSet() )
            {
                const std::string& name = input->eval( _featureNameExpr.mutable_value(), &context );
                osgGeom->setName( name );
            }

            // build the geometry:
            osg::Vec3Array* allPoints = 0L;

            if ( renderType == Geometry::TYPE_POLYGON )
            {
                buildPolygon(part, featureSRS, mapSRS, makeECEF, true, osgGeom);
                allPoints = static_cast<osg::Vec3Array*>( osgGeom->getVertexArray() );
            }
            else
            {
                // line or point geometry
                GLenum primMode = 
                    renderType == Geometry::TYPE_LINESTRING ? GL_LINE_STRIP :
                    renderType == Geometry::TYPE_RING       ? GL_LINE_LOOP :
                    GL_POINTS;
                allPoints = new osg::Vec3Array();
                transformAndLocalize( part->asVector(), featureSRS, allPoints, mapSRS, _world2local, makeECEF );
                osgGeom->addPrimitiveSet( new osg::DrawArrays( primMode, 0, part->size() ) );
                osgGeom->setVertexArray( allPoints );

                applyLineAndPointSymbology( osgGeom->getOrCreateStateSet(), lineSymbol, pointSymbol );

                if ( primMode == GL_POINTS && allPoints->size() == 1 )
                {
                    const osg::Vec3d& center = (*allPoints)[0];
                    osgGeom->setInitialBound( osg::BoundingBox(center-osg::Vec3(.5,.5,.5), center+osg::Vec3(.5,.5,.5)) );
                }
            }

            if (allPoints->getVertexBufferObject())
                allPoints->getVertexBufferObject()->setUsage(GL_STATIC_DRAW_ARB);
            
            // subdivide the mesh if necessary to conform to an ECEF globe:
            if ( makeECEF && renderType != Geometry::TYPE_POINTSET )
            {
                // check for explicit tessellation disable:
                const LineSymbol* line = _style.get<LineSymbol>();
                bool disableTess = line && line->tessellation().isSetTo(0);

                if ( makeECEF && !disableTess )
                {                    
                    double threshold = osg::DegreesToRadians( *_maxAngle_deg );
                    OE_DEBUG << "Running mesh subdivider with threshold " << *_maxAngle_deg << std::endl;

                    MeshSubdivider ms( _world2local, _local2world );
                    //ms.setMaxElementsPerEBO( INT_MAX );
                    if ( input->geoInterp().isSet() )
                        ms.run( *osgGeom, threshold, *input->geoInterp() );
                    else
                        ms.run( *osgGeom, threshold, *_geoInterp );
                }
            }


            // assign the primary color:
#if USE_SINGLE_COLOR            
            osg::Vec4Array* colors = new osg::Vec4Array( 1 );
            (*colors)[0] = primaryColor;
            osgGeom->setColorBinding( osg::Geometry::BIND_OVERALL );
#else

            osg::Vec4Array* colors = new osg::Vec4Array( osgGeom->getVertexArray()->getNumElements() ); //allPoints->size() );
            for(unsigned c=0; c<colors->size(); ++c)
                (*colors)[c] = primaryColor;
            osgGeom->setColorBinding( osg::Geometry::BIND_PER_VERTEX );
#endif


            osgGeom->setColorArray( colors );
            

            _geode->addDrawable( osgGeom );

            // record the geometry's primitive set(s) in the index:
            if ( context.featureIndex() )
                context.featureIndex()->tagPrimitiveSets( osgGeom, input );

            // build secondary geometry, if necessary (polygon outlines)
            if ( renderType == Geometry::TYPE_POLYGON && lineSymbol )
            {
                // polygon offset on the poly so the outline doesn't z-fight
                osgGeom->getOrCreateStateSet()->setAttributeAndModes( new osg::PolygonOffset(1,1), 1 );

                osg::Geometry* outline = new osg::Geometry();
                outline->setUseVertexBufferObjects( _useVertexBufferObjects.value() );

                buildPolygon(part, featureSRS, mapSRS, makeECEF, false, outline);

                if ( outline->getVertexArray()->getVertexBufferObject() )
                    outline->getVertexArray()->getVertexBufferObject()->setUsage(GL_STATIC_DRAW_ARB);                
                
                osg::Vec4f outlineColor = lineSymbol->stroke()->color();                

                osg::Vec4Array* outlineColors = new osg::Vec4Array();                
#if USE_SINGLE_COLOR
                outlineColors->reserve(1);
                outlineColors->push_back( outlineColor );
                outline->setColorBinding( osg::Geometry::BIND_OVERALL );
#else
                unsigned pcount = part->getTotalPointCount();                
                outlineColors->reserve( pcount );
                for( unsigned c=0; c < pcount; ++c )
                    outlineColors->push_back( outlineColor );
                outline->setColorBinding( osg::Geometry::BIND_PER_VERTEX );
#endif
                outline->setColorArray(outlineColors);

                // check for explicit tessellation disable:                
                bool disableTess = lineSymbol && lineSymbol->tessellation().isSetTo(0);

                // subdivide if necessary.                
                if ( makeECEF && !disableTess )
                {
                    double threshold = osg::DegreesToRadians( *_maxAngle_deg );
                    OE_DEBUG << "Running mesh subdivider for outlines with threshold " << *_maxAngle_deg << std::endl;
                    MeshSubdivider ms( _world2local, _local2world );
                    if ( input->geoInterp().isSet() )
                        ms.run( *outline, threshold, *input->geoInterp() );
                    else
                        ms.run( *outline, threshold, *_geoInterp );
                }

                applyLineAndPointSymbology( outline->getOrCreateStateSet(), lineSymbol, 0L );

                // make normals before adding an outline
                osgUtil::SmoothingVisitor sv;
                _geode->accept( sv );

                _geode->addDrawable( outline );

                //_featureNode->addDrawable( outline, input->getFID() );

                // Mark each primitive set with its feature ID.
                if ( context.featureIndex() )
                    context.featureIndex()->tagPrimitiveSets( outline, input );
            }

        }
    }
    
    return true;
}
Example #7
0
osg::Node*
PolygonizeLinesFilter::push(FeatureList& input, FilterContext& cx)
{
    // compute the coordinate localization matrices.
    computeLocalizers( cx );

    // establish some things
    bool                    makeECEF   = false;
    const SpatialReference* featureSRS = 0L;
    const SpatialReference* mapSRS     = 0L;

    if ( cx.isGeoreferenced() )
    {
        makeECEF   = cx.getSession()->getMapInfo().isGeocentric();
        featureSRS = cx.extent()->getSRS();
        mapSRS     = cx.getSession()->getMapInfo().getProfile()->getSRS();
    }

    // The operator we'll use to make lines into polygons.
    const LineSymbol* line = _style.get<LineSymbol>();
    PolygonizeLinesOperator polygonize( line ? (*line->stroke()) : Stroke() );

    // Geode to hold all the geometries.
    osg::Geode* geode = new PixelScalingGeode(); //osg::Geode();

    // iterate over all features.
    for( FeatureList::iterator i = input.begin(); i != input.end(); ++i )
    {
        Feature* f = i->get();

        // iterate over all the feature's geometry parts. We will treat
        // them as lines strings.
        GeometryIterator parts( f->getGeometry(), false );
        while( parts.hasMore() )
        {
            Geometry* part = parts.next();

            // skip empty geometry
            if ( part->size() == 0 )
                continue;

            // transform the geometry into the target SRS and localize it about 
            // a local reference point.
            osg::Vec3Array* verts   = new osg::Vec3Array();
            osg::Vec3Array* normals = new osg::Vec3Array();
            transformAndLocalize( part->asVector(), featureSRS, verts, normals, mapSRS, _world2local, makeECEF );

            // turn the lines into polygons.
            osg::Geometry* geom = polygonize( verts, normals );

            // install.
            geode->addDrawable( geom );

            // record the geometry's primitive set(s) in the index:
            if ( cx.featureIndex() )
                cx.featureIndex()->tagDrawable( geom, f );
        }
    }

    // attempt to combine geometries for better performance
    MeshConsolidator::run( *geode );

    // GPU performance optimization:
    VertexCacheOptimizer vco;
    geode->accept( vco );

    // If we're auto-scaling, we need a shader
    polygonize.installShaders( geode );

    return delocalize( geode );
}
bool
ExtrudeGeometryFilter::process( FeatureList& features, FilterContext& context )
{
    // seed our random number generators
    Random wallSkinPRNG( _wallSkinSymbol.valid()? *_wallSkinSymbol->randomSeed() : 0, Random::METHOD_FAST );
    Random roofSkinPRNG( _roofSkinSymbol.valid()? *_roofSkinSymbol->randomSeed() : 0, Random::METHOD_FAST );

    for( FeatureList::iterator f = features.begin(); f != features.end(); ++f )
    {
        Feature* input = f->get();

        GeometryIterator iter( input->getGeometry(), false );
        while( iter.hasMore() )
        {
            Geometry* part = iter.next();

            osg::ref_ptr<osg::Geometry> walls = new osg::Geometry();
            walls->setUseVertexBufferObjects( _useVertexBufferObjects.get() );
            
            osg::ref_ptr<osg::Geometry> rooflines = 0L;
            osg::ref_ptr<osg::Geometry> baselines = 0L;
            osg::ref_ptr<osg::Geometry> outlines  = 0L;
            
            if ( part->getType() == Geometry::TYPE_POLYGON )
            {
                rooflines = new osg::Geometry();
                rooflines->setUseVertexBufferObjects( _useVertexBufferObjects.get() );

                // prep the shapes by making sure all polys are open:
                static_cast<Polygon*>(part)->open();
            }

            // fire up the outline geometry if we have a line symbol.
            if ( _outlineSymbol != 0L )
            {
                outlines = new osg::Geometry();
                outlines->setUseVertexBufferObjects( _useVertexBufferObjects.get() );
            }

            // make a base cap if we're doing stencil volumes.
            if ( _makeStencilVolume )
            {
                baselines = new osg::Geometry();
                baselines->setUseVertexBufferObjects( _useVertexBufferObjects.get() );
            }

            // calculate the extrusion height:
            float height;

            if ( _heightCallback.valid() )
            {
                height = _heightCallback->operator()(input, context);
            }
            else if ( _heightExpr.isSet() )
            {
                height = input->eval( _heightExpr.mutable_value(), &context );
            }
            else
            {
                height = *_extrusionSymbol->height();
            }

            // calculate the height offset from the base:
            float offset = 0.0;
            if ( _heightOffsetExpr.isSet() )
            {
                offset = input->eval( _heightOffsetExpr.mutable_value(), &context );
            }

            osg::ref_ptr<osg::StateSet> wallStateSet;
            osg::ref_ptr<osg::StateSet> roofStateSet;

            // calculate the wall texturing:
            SkinResource* wallSkin = 0L;
            if ( _wallSkinSymbol.valid() )
            {
                if ( _wallResLib.valid() )
                {
                    SkinSymbol querySymbol( *_wallSkinSymbol.get() );
                    querySymbol.objectHeight() = fabs(height) - offset;
                    wallSkin = _wallResLib->getSkin( &querySymbol, wallSkinPRNG, context.getDBOptions() );
                }

                else
                {
                    //TODO: simple single texture?
                }
            }

            // calculate the rooftop texture:
            SkinResource* roofSkin = 0L;
            if ( _roofSkinSymbol.valid() )
            {
                if ( _roofResLib.valid() )
                {
                    SkinSymbol querySymbol( *_roofSkinSymbol.get() );
                    roofSkin = _roofResLib->getSkin( &querySymbol, roofSkinPRNG, context.getDBOptions() );
                }

                else
                {
                    //TODO: simple single texture?
                }
            }

            // calculate the colors:
            osg::Vec4f wallColor(1,1,1,0), wallBaseColor(1,1,1,0), roofColor(1,1,1,0), outlineColor(1,1,1,1);

            if ( _wallPolygonSymbol.valid() )
            {
                wallColor = _wallPolygonSymbol->fill()->color();
                if ( _extrusionSymbol->wallGradientPercentage().isSet() )
                {
                    wallBaseColor = Color(wallColor).brightness( 1.0 - *_extrusionSymbol->wallGradientPercentage() );
                }
                else
                {
                    wallBaseColor = wallColor;
                }
            }
            if ( _roofPolygonSymbol.valid() )
            {
                roofColor = _roofPolygonSymbol->fill()->color();
            }
            if ( _outlineSymbol.valid() )
            {
                outlineColor = _outlineSymbol->stroke()->color();
            }

            // Create the extruded geometry!
            if (extrudeGeometry( 
                    part, height, offset, 
                    *_extrusionSymbol->flatten(),
                    walls.get(), rooflines.get(), baselines.get(), outlines.get(),
                    wallColor, wallBaseColor, roofColor, outlineColor,
                    wallSkin, roofSkin,
                    context ) )
            {      
                if ( wallSkin )
                {
                    context.resourceCache()->getStateSet( wallSkin, wallStateSet );
                }

                // generate per-vertex normals, altering the geometry as necessary to avoid
                // smoothing around sharp corners
                osgUtil::SmoothingVisitor::smooth(
                    *walls.get(), 
                    osg::DegreesToRadians(_wallAngleThresh_deg) );

                // tessellate and add the roofs if necessary:
                if ( rooflines.valid() )
                {
                    osgUtil::Tessellator tess;
                    tess.setTessellationType( osgUtil::Tessellator::TESS_TYPE_GEOMETRY );
                    tess.setWindingType( osgUtil::Tessellator::TESS_WINDING_ODD );
                    tess.retessellatePolygons( *(rooflines.get()) );

                    // generate default normals (no crease angle necessary; they are all pointing up)
                    // TODO do this manually; probably faster
                    if ( !_makeStencilVolume )
                        osgUtil::SmoothingVisitor::smooth( *rooflines.get() );

                    if ( roofSkin )
                    {
                        context.resourceCache()->getStateSet( roofSkin, roofStateSet );
                    }
                }

                if ( baselines.valid() )
                {
                    osgUtil::Tessellator tess;
                    tess.setTessellationType( osgUtil::Tessellator::TESS_TYPE_GEOMETRY );
                    tess.setWindingType( osgUtil::Tessellator::TESS_WINDING_ODD );
                    tess.retessellatePolygons( *(baselines.get()) );
                }

                std::string name;
                if ( !_featureNameExpr.empty() )
                    name = input->eval( _featureNameExpr, &context );

                FeatureSourceIndex* index = context.featureIndex();

                addDrawable( walls.get(), wallStateSet.get(), name, input, index );

                if ( rooflines.valid() )
                {
                    addDrawable( rooflines.get(), roofStateSet.get(), name, input, index );
                }

                if ( baselines.valid() )
                {
                    addDrawable( baselines.get(), 0L, name, input, index );
                }

                if ( outlines.valid() )
                {
                    addDrawable( outlines.get(), 0L, name, input, index );
                }
            }   
        }
    }

    return true;
}
    //override
    bool renderFeaturesForStyle(
        const Style&       style,
        const FeatureList& features,
        osg::Referenced*   buildData,
        const GeoExtent&   imageExtent,
        osg::Image*        image )
    {
        // A processing context to use with the filters:
        FilterContext context;
        context.setProfile( getFeatureSource()->getFeatureProfile() );

        const LineSymbol*    masterLine = style.getSymbol<LineSymbol>();
        const PolygonSymbol* masterPoly = style.getSymbol<PolygonSymbol>();

        // sort into bins, making a copy for lines that require buffering.
        FeatureList polygons;
        FeatureList lines;

        for(FeatureList::const_iterator f = features.begin(); f != features.end(); ++f)
        {
            if ( f->get()->getGeometry() )
            {
                if ( masterPoly || f->get()->style()->has<PolygonSymbol>() )
                {
                    polygons.push_back( f->get() );
                }

                if ( masterLine || f->get()->style()->has<LineSymbol>() )
                {
                    Feature* newFeature = new Feature( *f->get() );
                    if ( !newFeature->getGeometry()->isLinear() )
                    {
                        newFeature->setGeometry( newFeature->getGeometry()->cloneAs(Geometry::TYPE_RING) );
                    }
                    lines.push_back( newFeature );
                }
            }
        }

        // initialize:
        RenderFrame frame;
        frame.xmin = imageExtent.xMin();
        frame.ymin = imageExtent.yMin();
        frame.xf   = (double)image->s() / imageExtent.width();
        frame.yf   = (double)image->t() / imageExtent.height();

        if ( lines.size() > 0 )
        {
            // We are buffering in the features native extent, so we need to use the
            // transformed extent to get the proper "resolution" for the image
            const SpatialReference* featureSRS = context.profile()->getSRS();
            GeoExtent transformedExtent = imageExtent.transform(featureSRS);

            double trans_xf = (double)image->s() / transformedExtent.width();
            double trans_yf = (double)image->t() / transformedExtent.height();

            // resolution of the image (pixel extents):
            double xres = 1.0/trans_xf;
            double yres = 1.0/trans_yf;

            // downsample the line data so that it is no higher resolution than to image to which
            // we intend to rasterize it. If you don't do this, you run the risk of the buffer 
            // operation taking forever on very high-res input data.
            if ( _options.optimizeLineSampling() == true )
            {
                ResampleFilter resample;
                resample.minLength() = osg::minimum( xres, yres );
                context = resample.push( lines, context );
            }

            // now run the buffer operation on all lines:
            BufferFilter buffer;
            double lineWidth = 1.0;
            if ( masterLine )
            {
                buffer.capStyle() = masterLine->stroke()->lineCap().value();

                if ( masterLine->stroke()->width().isSet() )
                {
                    lineWidth = masterLine->stroke()->width().value();

                    GeoExtent imageExtentInFeatureSRS = imageExtent.transform(featureSRS);
                    double pixelWidth = imageExtentInFeatureSRS.width() / (double)image->s();

                    // if the width units are specified, process them:
                    if (masterLine->stroke()->widthUnits().isSet() &&
                        masterLine->stroke()->widthUnits().get() != Units::PIXELS)
                    {
                        const Units& featureUnits = featureSRS->getUnits();
                        const Units& strokeUnits  = masterLine->stroke()->widthUnits().value();

                        // if the units are different than those of the feature data, we need to
                        // do a units conversion.
                        if ( featureUnits != strokeUnits )
                        {
                            if ( Units::canConvert(strokeUnits, featureUnits) )
                            {
                                // linear to linear, no problem
                                lineWidth = strokeUnits.convertTo( featureUnits, lineWidth );
                            }
                            else if ( strokeUnits.isLinear() && featureUnits.isAngular() )
                            {
                                // linear to angular? approximate degrees per meter at the 
                                // latitude of the tile's centroid.
                                lineWidth = masterLine->stroke()->widthUnits()->convertTo(Units::METERS, lineWidth);
                                double circ = featureSRS->getEllipsoid()->getRadiusEquator() * 2.0 * osg::PI;
                                double x, y;
                                context.profile()->getExtent().getCentroid(x, y);
                                double radians = (lineWidth/circ) * cos(osg::DegreesToRadians(y));
                                lineWidth = osg::RadiansToDegrees(radians);
                            }
                        }

                        // enfore a minimum width of one pixel.
                        float minPixels = masterLine->stroke()->minPixels().getOrUse( 1.0f );
                        lineWidth = osg::clampAbove(lineWidth, pixelWidth*minPixels);
                    }

                    else // pixels
                    {
                        lineWidth *= pixelWidth;
                    }
                }
            }

            buffer.distance() = lineWidth * 0.5;   // since the distance is for one side
            buffer.push( lines, context );
        }

        // Transform the features into the map's SRS:
        TransformFilter xform( imageExtent.getSRS() );
        xform.setLocalizeCoordinates( false );
        FilterContext polysContext = xform.push( polygons, context );
        FilterContext linesContext = xform.push( lines, context );

        // set up the AGG renderer:
        agg::rendering_buffer rbuf( image->data(), image->s(), image->t(), image->s()*4 );

        // Create the renderer and the rasterizer
        agg::renderer<agg::span_abgr32> ren(rbuf);
        agg::rasterizer ras;

        // Setup the rasterizer
        ras.gamma(1.3);
        ras.filling_rule(agg::fill_even_odd);

        // construct an extent for cropping the geometry to our tile.
        // extend just outside the actual extents so we don't get edge artifacts:
        GeoExtent cropExtent = GeoExtent(imageExtent);
        cropExtent.scale(1.1, 1.1);

        osg::ref_ptr<Symbology::Polygon> cropPoly = new Symbology::Polygon( 4 );
        cropPoly->push_back( osg::Vec3d( cropExtent.xMin(), cropExtent.yMin(), 0 ));
        cropPoly->push_back( osg::Vec3d( cropExtent.xMax(), cropExtent.yMin(), 0 ));
        cropPoly->push_back( osg::Vec3d( cropExtent.xMax(), cropExtent.yMax(), 0 ));
        cropPoly->push_back( osg::Vec3d( cropExtent.xMin(), cropExtent.yMax(), 0 ));

        // render the polygons
        for(FeatureList::iterator i = polygons.begin(); i != polygons.end(); i++)
        {
            Feature*  feature  = i->get();
            Geometry* geometry = feature->getGeometry();

            osg::ref_ptr<Geometry> croppedGeometry;
            if ( geometry->crop( cropPoly.get(), croppedGeometry ) )
            {
                const PolygonSymbol* poly =
                    feature->style().isSet() && feature->style()->has<PolygonSymbol>() ? feature->style()->get<PolygonSymbol>() :
                    masterPoly;
                
                const osg::Vec4 color = poly ? static_cast<osg::Vec4>(poly->fill()->color()) : osg::Vec4(1,1,1,1);
                rasterize(croppedGeometry.get(), color, frame, ras, ren);
            }
        }

        // render the lines
        for(FeatureList::iterator i = lines.begin(); i != lines.end(); i++)
        {
            Feature*  feature  = i->get();
            Geometry* geometry = feature->getGeometry();

            osg::ref_ptr<Geometry> croppedGeometry;
            if ( geometry->crop( cropPoly.get(), croppedGeometry ) )
            {
                const LineSymbol* line =
                    feature->style().isSet() && feature->style()->has<LineSymbol>() ? feature->style()->get<LineSymbol>() :
                    masterLine;
                
                const osg::Vec4 color = line ? static_cast<osg::Vec4>(line->stroke()->color()) : osg::Vec4(1,1,1,1);
                rasterize(croppedGeometry.get(), color, frame, ras, ren);
            }
        }

        return true;
    }
osg::Geode*
BuildGeometryFilter::processPolygons(FeatureList& features, const FilterContext& context)
{
    osg::Geode* geode = new osg::Geode();

    bool makeECEF = false;
    const SpatialReference* featureSRS = 0L;
    const SpatialReference* mapSRS = 0L;

    // set up the reference system info:
    if ( context.isGeoreferenced() )
    {
        makeECEF   = context.getSession()->getMapInfo().isGeocentric();
        featureSRS = context.extent()->getSRS();
        mapSRS     = context.getSession()->getMapInfo().getProfile()->getSRS();
    }

    for( FeatureList::iterator f = features.begin(); f != features.end(); ++f )
    {
        Feature* input = f->get();

        GeometryIterator parts( input->getGeometry(), false );
        while( parts.hasMore() )
        {
            Geometry* part = parts.next();

            // skip geometry that is invalid for a polygon
            if ( part->size() < 3 )
                continue;

            // access the polygon symbol, and bail out if there isn't one
            const PolygonSymbol* poly =
                input->style().isSet() && input->style()->has<PolygonSymbol>() ? input->style()->get<PolygonSymbol>() :
                _style.get<PolygonSymbol>();
            if ( !poly )
                continue;

            // resolve the color:
            osg::Vec4f primaryColor = poly->fill()->color();
            
            osg::ref_ptr<osg::Geometry> osgGeom = new osg::Geometry();
            osgGeom->setUseVertexBufferObjects( true );
            osgGeom->setUseDisplayList( false );

            // are we embedding a feature name?
            if ( _featureNameExpr.isSet() )
            {
                const std::string& name = input->eval( _featureNameExpr.mutable_value(), &context );
                osgGeom->setName( name );
            }

            // build the geometry:
            buildPolygon(part, featureSRS, mapSRS, makeECEF, true, osgGeom);

            osg::Vec3Array* allPoints = static_cast<osg::Vec3Array*>(osgGeom->getVertexArray());
            
            // subdivide the mesh if necessary to conform to an ECEF globe:
            if ( makeECEF )
            {
                double threshold = osg::DegreesToRadians( *_maxAngle_deg );
                OE_DEBUG << "Running mesh subdivider with threshold " << *_maxAngle_deg << std::endl;

                MeshSubdivider ms( _world2local, _local2world );
                //ms.setMaxElementsPerEBO( INT_MAX );
                if ( input->geoInterp().isSet() )
                    ms.run( *osgGeom, threshold, *input->geoInterp() );
                else
                    ms.run( *osgGeom, threshold, *_geoInterp );
            }

            // assign the primary color array. PER_VERTEX required in order to support
            // vertex optimization later
            osg::Vec4Array* colors = new osg::Vec4Array;
            colors->assign( osgGeom->getVertexArray()->getNumElements(), primaryColor );
            osgGeom->setColorArray( colors );
            osgGeom->setColorBinding( osg::Geometry::BIND_PER_VERTEX );

            geode->addDrawable( osgGeom );

            // record the geometry's primitive set(s) in the index:
            if ( context.featureIndex() )
                context.featureIndex()->tagPrimitiveSets( osgGeom, input );
        }
    }
    
    return geode;
}
    FeatureCursor* createFeatureCursor(const Symbology::Query& query, ProgressCallback* progress)
    {
        FeatureCursor* result = 0L;

        std::string url = createURL( query );

        // the URL wil lbe empty if it was invalid or outside the level bounds of the layer.
        if (url.empty())
            return 0L;

        OE_DEBUG << LC << url << std::endl;
        URI uri(url, _options.url()->context());

        // read the data:
        ReadResult r = uri.readString(_readOptions.get(), progress);

        const std::string& buffer = r.getString();
        const Config&      meta   = r.metadata();

        bool dataOK = false;

        FeatureList features;
        if ( !buffer.empty() )
        {
            // Get the mime-type from the metadata record if possible
            std::string mimeType = r.metadata().value( IOMetadata::CONTENT_TYPE );
            //If the mimetype is empty then try to set it from the format specification
            if (mimeType.empty())
            {
                if (_options.format().value() == "json") mimeType = "json";
                else if (_options.format().value().compare("gml") == 0) mimeType = "text/xml";
                else if (_options.format().value().compare("pbf") == 0) mimeType = "application/x-protobuf";
            }
            dataOK = getFeatures( buffer, *query.tileKey(), mimeType, features );
        }

        if ( dataOK )
        {
            OE_DEBUG << LC << "Read " << features.size() << " features" << std::endl;
        }

        //If we have any filters, process them here before the cursor is created
        if (getFilters() && !getFilters()->empty() && !features.empty())
        {
            FilterContext cx;
            cx.setProfile(getFeatureProfile());
            cx.extent() = query.tileKey()->getExtent();

            for (FeatureFilterChain::const_iterator i = getFilters()->begin(); i != getFilters()->end(); ++i)
            {
                FeatureFilter* filter = i->get();
                cx = filter->push(features, cx);
            }
        }

        // If we have any features and we have an fid attribute, override the fid of the features
        if (_options.fidAttribute().isSet())
        {
            for (FeatureList::iterator itr = features.begin(); itr != features.end(); ++itr)
            {
                std::string attr = itr->get()->getString(_options.fidAttribute().get());                
                FeatureID fid = as<long>(attr, 0);
                itr->get()->setFID( fid );
            }
        }

        result = new FeatureListCursor(features);
        return result;
    }
    FilterContext push(FeatureList& input, FilterContext& context)
    {
        if (_featureSource.valid())
        {
            // Get any features that intersect this query.
            FeatureList boundaries;
            getFeatures(context.extent().get(), boundaries );
            
            
            // The list of output features
            FeatureList output;

            if (boundaries.empty())
            {
                // No intersecting features.  If contains is false, then just the output to the input.
                if (contains() == false)
                {
                    output = input;
                }
            }
            else
            {
                // Transform the boundaries into the coordinate system of the features
                for (FeatureList::iterator itr = boundaries.begin(); itr != boundaries.end(); ++itr)
                {
                    itr->get()->transform( context.profile()->getSRS() );
                }

                for(FeatureList::const_iterator f = input.begin(); f != input.end(); ++f)
                {
                    Feature* feature = f->get();
                    if ( feature && feature->getGeometry() )
                    {
                        osg::Vec2d c = feature->getGeometry()->getBounds().center2d();

                        if ( contains() == true )
                        {
                            // coarsest:
                            if (_featureSource->getFeatureProfile()->getExtent().contains(GeoPoint(feature->getSRS(), c.x(), c.y())))
                            {
                                for (FeatureList::iterator itr = boundaries.begin(); itr != boundaries.end(); ++itr)
                                {
                                    Ring* ring = dynamic_cast< Ring*>(itr->get()->getGeometry());
                                    if (ring && ring->contains2D(c.x(), c.y()))
                                    {
                                        output.push_back( feature );
                                    }
                                }                        
                            }
                        }

                        else
                        {    
                            bool contained = false;

                            // coarsest:
                            if (_featureSource->getFeatureProfile()->getExtent().contains(GeoPoint(feature->getSRS(), c.x(), c.y())))
                            {
                                for (FeatureList::iterator itr = boundaries.begin(); itr != boundaries.end(); ++itr)
                                {
                                    Ring* ring = dynamic_cast< Ring*>(itr->get()->getGeometry());
                                    if (ring && ring->contains2D(c.x(), c.y()))
                                    {                             
                                        contained = true;
                                        break;
                                    }
                                }
                            }
                            if ( !contained )
                            {
                                output.push_back( feature );
                            }
                        }
                    }
                }
            }

            OE_INFO << LC << "Allowed " << output.size() << " out of " << input.size() << " features\n";

            input = output;
        }

        return context;
    }
    //override
    bool renderFeaturesForStyle(
        const Style&       style,
        const FeatureList& inFeatures,
        osg::Referenced*   buildData,
        const GeoExtent&   imageExtent,
        osg::Image*        image )
    {
        // local copy of the features that we can process
        FeatureList features = inFeatures;

        BuildData* bd = static_cast<BuildData*>( buildData );

        // A processing context to use with the filters:
        FilterContext context;
        context.profile() = getFeatureSource()->getFeatureProfile();

        const LineSymbol* masterLine = style.getSymbol<LineSymbol>();
        const PolygonSymbol* masterPoly = style.getSymbol<PolygonSymbol>();

        //bool embeddedStyles = getFeatureSource()->hasEmbeddedStyles();

        // if only a line symbol exists, and there are polygons in the mix, draw them
        // as outlines (line rings).
        //OE_INFO << LC << "Line Symbol = " << (masterLine == 0L ? "null" : masterLine->getConfig().toString()) << std::endl;
        //OE_INFO << LC << "Poly SYmbol = " << (masterPoly == 0L ? "null" : masterPoly->getConfig().toString()) << std::endl;

        //bool convertPolysToRings = poly == 0L && line != 0L;
        //if ( convertPolysToRings )
        //    OE_INFO << LC << "No PolygonSymbol; will draw polygons to rings" << std::endl;

        // initialize:
        double xmin = imageExtent.xMin();
        double ymin = imageExtent.yMin();
        //double s = (double)image->s();
        //double t = (double)image->t();
        double xf = (double)image->s() / imageExtent.width();
        double yf = (double)image->t() / imageExtent.height();

        // strictly speaking we should iterate over the features and buffer each one that's a line,
        // rather then checking for the existence of a LineSymbol.
        FeatureList linesToBuffer;
        for(FeatureList::iterator i = features.begin(); i != features.end(); i++)
        {
            Feature* feature = i->get();
            Geometry* geom = feature->getGeometry();

            if ( geom )
            {
                // check for an embedded style:
                const LineSymbol* line = feature->style().isSet() ? 
                    feature->style()->getSymbol<LineSymbol>() : masterLine;

                const PolygonSymbol* poly =
                    feature->style().isSet() ? feature->style()->getSymbol<PolygonSymbol>() : masterPoly;

                // if we have polygons but only a LineSymbol, draw the poly as a line.
                if ( geom->getComponentType() == Geometry::TYPE_POLYGON )
                {
                    if ( !poly && line )
                    {
                        Feature* outline = new Feature( *feature );
                        geom = geom->cloneAs( Geometry::TYPE_RING );
                        outline->setGeometry( geom );
                        *i = outline;
                        feature = outline;
                    }
                    //TODO: fix to enable outlined polys. doesn't work, not sure why -gw
                    //else if ( poly && line )
                    //{
                    //    Feature* outline = new Feature();
                    //    geom = geom->cloneAs( Geometry::TYPE_LINESTRING );
                    //    outline->setGeometry( geom );
                    //    features.push_back( outline );
                    //}
                }

                bool needsBuffering =
                    geom->getComponentType() == Geometry::TYPE_LINESTRING || 
                    geom->getComponentType() == Geometry::TYPE_RING;

                if ( needsBuffering )
                {
                    linesToBuffer.push_back( feature );
                }
            }
        }

        if ( linesToBuffer.size() > 0 )
        {
            //We are buffering in the features native extent, so we need to use the transform extent to get the proper "resolution" for the image
            GeoExtent transformedExtent = imageExtent.transform(context.profile()->getSRS());

            double trans_xf = (double)image->s() / transformedExtent.width();
            double trans_yf = (double)image->t() / transformedExtent.height();

            // resolution of the image (pixel extents):
            double xres = 1.0/trans_xf;
            double yres = 1.0/trans_yf;

            // downsample the line data so that it is no higher resolution than to image to which
            // we intend to rasterize it. If you don't do this, you run the risk of the buffer 
            // operation taking forever on very high-res input data.
            if ( _options.optimizeLineSampling() == true )
            {
                ResampleFilter resample;
                resample.minLength() = osg::minimum( xres, yres );
                context = resample.push( linesToBuffer, context );
            }

            // now run the buffer operation on all lines:
            BufferFilter buffer;
            float lineWidth = 0.5;
            if ( masterLine )
            {
                buffer.capStyle() = masterLine->stroke()->lineCap().value();

                if ( masterLine->stroke()->width().isSet() )
                    lineWidth = masterLine->stroke()->width().value();
            }

            // "relative line size" means that the line width is expressed in (approx) pixels
            // rather than in map units
            if ( _options.relativeLineSize() == true )
                buffer.distance() = xres * lineWidth;
            else
                buffer.distance() = lineWidth;

            buffer.push( linesToBuffer, context );
        }

        // First, transform the features into the map's SRS:
        TransformFilter xform( imageExtent.getSRS() );
        xform.setLocalizeCoordinates( false );
        context = xform.push( features, context );

        // set up the AGG renderer:
        agg::rendering_buffer rbuf( image->data(), image->s(), image->t(), image->s()*4 );

        // Create the renderer and the rasterizer
        agg::renderer<agg::span_abgr32> ren(rbuf);
        agg::rasterizer ras;

        // Setup the rasterizer
        ras.gamma(1.3);
        ras.filling_rule(agg::fill_even_odd);

        GeoExtent cropExtent = GeoExtent(imageExtent);
        cropExtent.scale(1.1, 1.1);

        osg::ref_ptr<Symbology::Polygon> cropPoly = new Symbology::Polygon( 4 );
        cropPoly->push_back( osg::Vec3d( cropExtent.xMin(), cropExtent.yMin(), 0 ));
        cropPoly->push_back( osg::Vec3d( cropExtent.xMax(), cropExtent.yMin(), 0 ));
        cropPoly->push_back( osg::Vec3d( cropExtent.xMax(), cropExtent.yMax(), 0 ));
        cropPoly->push_back( osg::Vec3d( cropExtent.xMin(), cropExtent.yMax(), 0 ));

        double lineWidth = 1.0;
        if ( masterLine )
            lineWidth = (double)masterLine->stroke()->width().value();

        osg::Vec4 color = osg::Vec4(1, 1, 1, 1);
        if ( masterLine )
            color = masterLine->stroke()->color();

        // render the features
        for(FeatureList::iterator i = features.begin(); i != features.end(); i++)
        {
            Feature* feature = i->get();
            //bool first = bd->_pass == 0 && i == features.begin();

            Geometry* geometry = feature->getGeometry();

            osg::ref_ptr< Geometry > croppedGeometry;
            if ( ! geometry->crop( cropPoly.get(), croppedGeometry ) )
                continue;

            // set up a default color:
            osg::Vec4 c = color;
            unsigned int a = (unsigned int)(127+(c.a()*255)/2); // scale alpha up
            agg::rgba8 fgColor( (unsigned int)(c.r()*255), (unsigned int)(c.g()*255), (unsigned int)(c.b()*255), a );

            GeometryIterator gi( croppedGeometry.get() );
            while( gi.hasMore() )
            {
                c = color;
                Geometry* g = gi.next();
            
                const LineSymbol* line = feature->style().isSet() ? 
                    feature->style()->getSymbol<LineSymbol>() : masterLine;

                const PolygonSymbol* poly =
                    feature->style().isSet() ? feature->style()->getSymbol<PolygonSymbol>() : masterPoly;

                if (g->getType() == Geometry::TYPE_RING || g->getType() == Geometry::TYPE_LINESTRING)
                {
                    if ( line )
                        c = line->stroke()->color();
                    else if ( poly )
                        c = poly->fill()->color();
                }

                else if ( g->getType() == Geometry::TYPE_POLYGON )
                {
                    if ( poly )
                        c = poly->fill()->color();
                    else if ( line )
                        c = line->stroke()->color();
                }

                a = (unsigned int)(127+(c.a()*255)/2); // scale alpha up
                fgColor = agg::rgba8( (unsigned int)(c.r()*255), (unsigned int)(c.g()*255), (unsigned int)(c.b()*255), a );

                ras.filling_rule( agg::fill_even_odd );
                for( Geometry::iterator p = g->begin(); p != g->end(); p++ )
                {
                    const osg::Vec3d& p0 = *p;
                    double x0 = xf*(p0.x()-xmin);
                    double y0 = yf*(p0.y()-ymin);

                    //const osg::Vec3d& p1 = p+1 != g->end()? *(p+1) : g->front();
                    //double x1 = xf*(p1.x()-xmin);
                    //double y1 = yf*(p1.y()-ymin);

                    if ( p == g->begin() )
                        ras.move_to_d( x0, y0 );
                    else
                        ras.line_to_d( x0, y0 );
                }
            }
            ras.render(ren, fgColor);
            ras.reset();
        }

        bd->_pass++;
        return true;            
    }
osg::Node*
PolygonizeLinesFilter::push(FeatureList& input, FilterContext& cx)
{
    // compute the coordinate localization matrices.
    computeLocalizers( cx );

    // establish some things
    bool                    makeECEF   = false;
    const SpatialReference* featureSRS = 0L;
    const SpatialReference* mapSRS     = 0L;

    if ( cx.isGeoreferenced() )
    {
        makeECEF   = cx.getSession()->getMapInfo().isGeocentric();
        featureSRS = cx.extent()->getSRS();
        mapSRS     = cx.getSession()->getMapInfo().getProfile()->getSRS();
    }

    // The operator we'll use to make lines into polygons.
    const LineSymbol* line = _style.get<LineSymbol>();
    PolygonizeLinesOperator polygonize( line ? (*line->stroke()) : Stroke() );

    // Geode to hold all the geometries.
    osg::Geode* geode = new osg::Geode();

    // iterate over all features.
    for( FeatureList::iterator i = input.begin(); i != input.end(); ++i )
    {
        Feature* f = i->get();

        // iterate over all the feature's geometry parts. We will treat
        // them as lines strings.
        GeometryIterator parts( f->getGeometry(), false );
        while( parts.hasMore() )
        {
            Geometry* part = parts.next();

            // skip empty geometry
            if ( part->size() == 0 )
                continue;

            // transform the geometry into the target SRS and localize it about 
            // a local reference point.
            osg::Vec3Array* verts   = new osg::Vec3Array();
            osg::Vec3Array* normals = new osg::Vec3Array();
            transformAndLocalize( part->asVector(), featureSRS, verts, normals, mapSRS, _world2local, makeECEF );

            // turn the lines into polygons.
            osg::Geometry* geom = polygonize( verts, normals );
            geode->addDrawable( geom );
        }
    }

    // attempt to combine geometries for better performance
    MeshConsolidator::run( *geode );

    // GPU performance optimization:
#if 0 // issue: ignores vertex attributes
    osgUtil::Optimizer optimizer;
    optimizer.optimize(
        result,
        osgUtil::Optimizer::VERTEX_PRETRANSFORM |
        osgUtil::Optimizer::VERTEX_POSTTRANSFORM );
#endif

    return delocalize( geode );
}
FilterContext
CropFilter::push( FeatureList& input, FilterContext& context )
{
    if ( !context.extent().isSet() )
    {
        OE_WARN << LC << "Extent is not set (and is required)" << std::endl;
        return context;
    }

    const GeoExtent& extent = *context.extent();

    GeoExtent newExtent( extent.getSRS() );

    if ( _method == METHOD_CENTROID )
    {
        for( FeatureList::iterator i = input.begin(); i != input.end();  )
        {
            bool keepFeature = false;

            Feature* feature = i->get();
            Geometry* featureGeom = feature->getGeometry();

            if ( featureGeom && featureGeom->isValid() )
            {
                Bounds bounds = featureGeom->getBounds();
                if ( bounds.isValid() )
                {
                    osg::Vec3d centroid = bounds.center();
                    if ( extent.contains( centroid.x(), centroid.y() ) )
                    {
                        keepFeature = true;
                        newExtent.expandToInclude( bounds.xMin(), bounds.yMin() );
                    }
                }
            }

            if ( keepFeature )
                ++i;
            else
                i = input.erase( i );
        }
    }

    else // METHOD_CROPPING (requires GEOS)
    {
#ifdef OSGEARTH_HAVE_GEOS

        // create the intersection polygon:
        osg::ref_ptr<Symbology::Polygon> poly;
        
        for( FeatureList::iterator i = input.begin(); i != input.end();  )
        {
            bool keepFeature = false;

            Feature* feature = i->get();

            Symbology::Geometry* featureGeom = feature->getGeometry();
            if ( featureGeom && featureGeom->isValid() )
            {
                // test for trivial acceptance:
                const Bounds bounds = featureGeom->getBounds();
                if ( !bounds.isValid() )
                {
                    //nop
                }

                else if ( extent.contains( bounds ) )
                {
                    keepFeature = true;
                    newExtent.expandToInclude( bounds );
                }

                // then move on to the cropping operation:
                else
                {
                    if ( !poly.valid() )
                    {
                        poly = new Symbology::Polygon();
                        poly->push_back( osg::Vec3d( extent.xMin(), extent.yMin(), 0 ));
                        poly->push_back( osg::Vec3d( extent.xMax(), extent.yMin(), 0 ));
                        poly->push_back( osg::Vec3d( extent.xMax(), extent.yMax(), 0 ));
                        poly->push_back( osg::Vec3d( extent.xMin(), extent.yMax(), 0 ));
                    }

                    osg::ref_ptr<Geometry> croppedGeometry;
                    if ( featureGeom->crop( poly.get(), croppedGeometry ) )
                    {
                        if ( croppedGeometry->isValid() )
                        {
                            feature->setGeometry( croppedGeometry.get() );
                            keepFeature = true;
                            newExtent.expandToInclude( croppedGeometry->getBounds() );
                        }
                    }
                }
            }

            if ( keepFeature )
                ++i;
            else
                i = input.erase( i );
        }  

#else // OSGEARTH_HAVE_GEOS

        OE_WARN << "CropFilter - METHOD_CROPPING not available - please compile osgEarth with GEOS" << std::endl;
        return context;

#endif
    }

    FilterContext newContext = context;
    newContext.extent() = newExtent;

    return newContext;
}
Example #16
0
void
FeatureNode::build()
{
    // if there's a decoration, clear it out first.
    this->clearDecoration();
    _attachPoint = 0L;

    // if there is existing geometry, kill it
    this->removeChildren( 0, this->getNumChildren() );

    if ( !getMapNode() )
        return;

    if ( _features.empty() )
        return;

    const Style &style = getStyle();

    // compilation options.
    GeometryCompilerOptions options = _options;
    
    // figure out what kind of altitude manipulation we need to perform.
    AnnotationUtils::AltitudePolicy ap;
    AnnotationUtils::getAltitudePolicy( style, ap );

    // If we're doing auto-clamping on the CPU, shut off compiler map clamping
    // clamping since it would be redundant.
    // TODO: I think this is OBE now that we have "scene" clamping technique..
    if ( ap.sceneClamping )
    {
        options.ignoreAltitudeSymbol() = true;
    }

    osg::Node* node = _compiled.get();
    if (_needsRebuild || !_compiled.valid() )
    {
        // Clone the Features before rendering as the GeometryCompiler and it's filters can change the coordinates
        // of the geometry when performing localization or converting to geocentric.
        _extent = GeoExtent::INVALID;

        FeatureList clone;
        for(FeatureList::iterator itr = _features.begin(); itr != _features.end(); ++itr)
        {
            Feature* feature = new Feature( *itr->get(), osg::CopyOp::DEEP_COPY_ALL);
            GeoExtent featureExtent(feature->getSRS(), feature->getGeometry()->getBounds());

            if (_extent.isInvalid())
            {
                _extent = featureExtent;
            }
            else
            {
                _extent.expandToInclude( featureExtent );
            }
            clone.push_back( feature );
        }

        // prep the compiler:
        GeometryCompiler compiler( options );
        Session* session = new Session( getMapNode()->getMap(), _styleSheet.get() );

        FilterContext context( session, new FeatureProfile( _extent ), _extent );

        _compiled = compiler.compile( clone, style, context );
        node = _compiled.get();
        _needsRebuild = false;

        // Compute the world bounds
        osg::BoundingSphered bounds;
        for( FeatureList::iterator itr = _features.begin(); itr != _features.end(); ++itr)
        {
            osg::BoundingSphered bs;
            itr->get()->getWorldBound(getMapNode()->getMapSRS(), bs);
            bounds.expandBy(bs);
        }
        // The polytope will ensure we only clamp to intersecting tiles:
        Feature::getWorldBoundingPolytope(bounds, getMapNode()->getMapSRS(), _featurePolytope);

    }

    if ( node )
    {
        if ( AnnotationUtils::styleRequiresAlphaBlending( style ) &&
             getStyle().get<ExtrusionSymbol>() )
        {
            node = AnnotationUtils::installTwoPassAlpha( node );
        }

        //OE_NOTICE << GeometryUtils::geometryToGeoJSON( _feature->getGeometry() ) << std::endl;

        _attachPoint = new osg::Group();
        _attachPoint->addChild( node );

        // Draped (projected) geometry
        if ( ap.draping )
        {
            DrapeableNode* d = new DrapeableNode(); // getMapNode() );
            d->addChild( _attachPoint );
            this->addChild( d );
        }

        // GPU-clamped geometry
        else if ( ap.gpuClamping )
        {
            ClampableNode* clampable = new ClampableNode( getMapNode() );
            clampable->addChild( _attachPoint );
            this->addChild( clampable );

            const RenderSymbol* render = style.get<RenderSymbol>();
            if ( render && render->depthOffset().isSet() )
            {
                clampable->setDepthOffsetOptions( *render->depthOffset() );
            }
        }

        else 
        {
            this->addChild( _attachPoint );

            // CPU-clamped geometry?
            if ( ap.sceneClamping )
            {
                // save for later when we need to reclamp the mesh on the CPU
                _altitude = style.get<AltitudeSymbol>();

                // activate the terrain callback:
                setCPUAutoClamping( true );

                // set default lighting based on whether we are extruding:
                setLightingIfNotSet( style.has<ExtrusionSymbol>() );

                // do an initial clamp to get started.
                clampMesh( getMapNode()->getTerrain()->getGraph() );
            } 

            applyRenderSymbology( style );
        }
    }

    updateClusterCulling();
}
osg::Geode*
BuildGeometryFilter::processPolygons(FeatureList& features, FilterContext& context)
{
    osg::Geode* geode = new osg::Geode();

    bool makeECEF = false;
    const SpatialReference* featureSRS = 0L;
    const SpatialReference* mapSRS = 0L;

    // set up the reference system info:
    if ( context.isGeoreferenced() )
    {
        makeECEF   = context.getSession()->getMapInfo().isGeocentric();
        featureSRS = context.extent()->getSRS();
        mapSRS     = context.getSession()->getMapInfo().getProfile()->getSRS();
    }

    for( FeatureList::iterator f = features.begin(); f != features.end(); ++f )
    {
        Feature* input = f->get();

        // access the polygon symbol, and bail out if there isn't one
        const PolygonSymbol* poly =
            input->style().isSet() && input->style()->has<PolygonSymbol>() ? input->style()->get<PolygonSymbol>() :
            _style.get<PolygonSymbol>();

        if ( !poly )
            continue;

        // run a symbol script if present.
        if ( poly->script().isSet() )
        {
            StringExpression temp( poly->script().get() );
            input->eval( temp, &context );
        }

        GeometryIterator parts( input->getGeometry(), false );
        while( parts.hasMore() )
        {
            Geometry* part = parts.next();

            part->removeDuplicates();

            // skip geometry that is invalid for a polygon
            if ( part->size() < 3 )
                continue;

            // resolve the color:
            osg::Vec4f primaryColor = poly->fill()->color();
            
            osg::ref_ptr<osg::Geometry> osgGeom = new osg::Geometry();
            //osgGeom->setUseVertexBufferObjects( true );
            //osgGeom->setUseDisplayList( false );

            // are we embedding a feature name?
            if ( _featureNameExpr.isSet() )
            {
                const std::string& name = input->eval( _featureNameExpr.mutable_value(), &context );
                osgGeom->setName( name );
            }


            // compute localizing matrices or use globals
            osg::Matrixd w2l, l2w;
            if (makeECEF)
            {
                osgEarth::GeoExtent featureExtent(featureSRS);
                featureExtent.expandToInclude(part->getBounds());

                computeLocalizers(context, featureExtent, w2l, l2w);
            }
            else
            {
                w2l = _world2local;
                l2w = _local2world;
            }


            // build the geometry:
            tileAndBuildPolygon(part, featureSRS, mapSRS, makeECEF, true, osgGeom, w2l);
            //buildPolygon(part, featureSRS, mapSRS, makeECEF, true, osgGeom, w2l);

            osg::Vec3Array* allPoints = static_cast<osg::Vec3Array*>(osgGeom->getVertexArray());
            if (allPoints && allPoints->size() > 0)
            {
                // subdivide the mesh if necessary to conform to an ECEF globe:
                if ( makeECEF )
                {
                    //convert back to world coords
                    for( osg::Vec3Array::iterator i = allPoints->begin(); i != allPoints->end(); ++i )
                    {
                        osg::Vec3d v(*i);
                        v = v * l2w;
                        v = v * _world2local;

                        (*i)._v[0] = v[0];
                        (*i)._v[1] = v[1];
                        (*i)._v[2] = v[2];
                    }

                    double threshold = osg::DegreesToRadians( *_maxAngle_deg );
                    OE_DEBUG << "Running mesh subdivider with threshold " << *_maxAngle_deg << std::endl;

                    MeshSubdivider ms( _world2local, _local2world );
                    if ( input->geoInterp().isSet() )
                        ms.run( *osgGeom, threshold, *input->geoInterp() );
                    else
                        ms.run( *osgGeom, threshold, *_geoInterp );
                }

                // assign the primary color array. PER_VERTEX required in order to support
                // vertex optimization later
                unsigned count = osgGeom->getVertexArray()->getNumElements();
                osg::Vec4Array* colors = new osg::Vec4Array;
                colors->assign( count, primaryColor );
                osgGeom->setColorArray( colors );
                osgGeom->setColorBinding( osg::Geometry::BIND_PER_VERTEX );

                geode->addDrawable( osgGeom );

                // record the geometry's primitive set(s) in the index:
                if ( context.featureIndex() )
                    context.featureIndex()->tagDrawable( osgGeom, input );
        
                // install clamping attributes if necessary
                if (_style.has<AltitudeSymbol>() &&
                    _style.get<AltitudeSymbol>()->technique() == AltitudeSymbol::TECHNIQUE_GPU)
                {            
                    Clamping::applyDefaultClampingAttrs( osgGeom, input->getDouble("__oe_verticalOffset", 0.0) );
                }
            }
        }
    }
    
    return geode;
}
Example #18
0
void
AltitudeFilter::pushAndClamp( FeatureList& features, FilterContext& cx )
{
    const Session* session = cx.getSession();

    // the map against which we'll be doing elevation clamping
    //MapFrame mapf = session->createMapFrame( Map::ELEVATION_LAYERS );
    MapFrame mapf = session->createMapFrame( 
        (Map::ModelParts)(Map::TERRAIN_LAYERS | Map::MODEL_LAYERS) );

    const SpatialReference* mapSRS = mapf.getProfile()->getSRS();
    osg::ref_ptr<const SpatialReference> featureSRS = cx.profile()->getSRS();

    // establish an elevation query interface based on the features' SRS.
    ElevationQuery eq( mapf );

    // want a result even if it's low res
    eq.setFallBackOnNoData( true );

    NumericExpression scaleExpr;
    if ( _altitude->verticalScale().isSet() )
        scaleExpr = *_altitude->verticalScale();

    NumericExpression offsetExpr;
    if ( _altitude->verticalOffset().isSet() )
        offsetExpr = *_altitude->verticalOffset();

    // whether to record the min/max height-above-terrain values.
    bool collectHATs =
        _altitude->clamping() == AltitudeSymbol::CLAMP_RELATIVE_TO_TERRAIN ||
        _altitude->clamping() == AltitudeSymbol::CLAMP_ABSOLUTE;

    // whether to clamp every vertex (or just the centroid)
    bool perVertex =
        _altitude->binding() == AltitudeSymbol::BINDING_VERTEX;

    // whether the SRS's have a compatible vertical datum.
    bool vertEquiv =
        featureSRS->isVertEquivalentTo( mapSRS );



    for( FeatureList::iterator i = features.begin(); i != features.end(); ++i )
    {
        Feature* feature = i->get();
        
        // run a symbol script if present.
        if ( _altitude.valid() && _altitude->script().isSet() )
        {
            StringExpression temp( _altitude->script().get() );
            feature->eval( temp, &cx );
        }

        double maxTerrainZ  = -DBL_MAX;
        double minTerrainZ  =  DBL_MAX;
        double minHAT       =  DBL_MAX;
        double maxHAT       = -DBL_MAX;

        double scaleZ = 1.0;
        if ( _altitude.valid() && _altitude->verticalScale().isSet() )
            scaleZ = feature->eval( scaleExpr, &cx );

        double offsetZ = 0.0;
        if ( _altitude.valid() && _altitude->verticalOffset().isSet() )
            offsetZ = feature->eval( offsetExpr, &cx );
        
        GeometryIterator gi( feature->getGeometry() );
        while( gi.hasMore() )
        {
            Geometry* geom = gi.next();

            // Absolute heights in Z. Only need to collect the HATs; the geometry
            // remains unchanged.
            if ( _altitude->clamping() == AltitudeSymbol::CLAMP_ABSOLUTE )
            {
                if ( perVertex )
                {
                    std::vector<double> elevations;
                    elevations.reserve( geom->size() );

                    if ( eq.getElevations( geom->asVector(), featureSRS, elevations, _maxRes ) )
                    {
                        for( unsigned i=0; i<geom->size(); ++i )
                        {
                            osg::Vec3d& p = (*geom)[i];

                            p.z() *= scaleZ;
                            p.z() += offsetZ;

                            double z = p.z();

                            if ( !vertEquiv )
                            {
                                osg::Vec3d tempgeo;
                                if ( !featureSRS->transform(p, mapSRS->getGeographicSRS(), tempgeo) )
                                    z = tempgeo.z();
                            }

                            double hat = z - elevations[i];

                            if ( hat > maxHAT )
                                maxHAT = hat;
                            if ( hat < minHAT )
                                minHAT = hat;

                            if ( elevations[i] > maxTerrainZ )
                                maxTerrainZ = elevations[i];
                            if ( elevations[i] < minTerrainZ )
                                minTerrainZ = elevations[i];
                        }
                    }
                }
                else // per centroid
                {
                    osgEarth::Bounds bounds = geom->getBounds();
                    const osg::Vec2d& center = bounds.center2d();
                    GeoPoint centroid(featureSRS, center.x(), center.y());
                    double   centroidElevation;

                    if ( eq.getElevation( centroid, centroidElevation, _maxRes ) )
                    {
                        for( unsigned i=0; i<geom->size(); ++i )
                        {
                            osg::Vec3d& p = (*geom)[i];
                            p.z() *= scaleZ;
                            p.z() += offsetZ;

                            double z = p.z();
                            if ( !vertEquiv )
                            {
                                osg::Vec3d tempgeo;
                                if ( !featureSRS->transform(p, mapSRS->getGeographicSRS(), tempgeo) )
                                    z = tempgeo.z();
                            }

                            double hat = z - centroidElevation;

                            if ( hat > maxHAT )
                                maxHAT = hat;
                            if ( hat < minHAT )
                                minHAT = hat;
                        }

                        if ( centroidElevation > maxTerrainZ )
                            maxTerrainZ = centroidElevation;
                        if ( centroidElevation < minTerrainZ )
                            minTerrainZ = centroidElevation;
                    }
                }
            }

            // Heights-above-ground in Z. Need to resolve this to an absolute number
            // and record HATs along the way.
            else if ( _altitude->clamping() == AltitudeSymbol::CLAMP_RELATIVE_TO_TERRAIN )
            {
                osg::ref_ptr<const SpatialReference> featureSRSwithMapVertDatum = !vertEquiv ?
                    SpatialReference::create(featureSRS->getHorizInitString(), mapSRS->getVertInitString()) : 0L;

                if ( perVertex )
                {
                    std::vector<double> elevations;
                    elevations.reserve( geom->size() );

                    if ( eq.getElevations( geom->asVector(), featureSRS, elevations, _maxRes ) )
                    {
                        for( unsigned i=0; i<geom->size(); ++i )
                        {
                            osg::Vec3d& p = (*geom)[i];

                            p.z() *= scaleZ;
                            p.z() += offsetZ;

                            double hat = p.z();
                            p.z() = elevations[i] + p.z();

                            // if necessary, convert the Z value (which is now in the map's SRS) back to
                            // the feature's SRS.
                            if ( !vertEquiv )
                            {
                                featureSRSwithMapVertDatum->transform(p, featureSRS, p);
                            }

                            if ( hat > maxHAT )
                                maxHAT = hat;
                            if ( hat < minHAT )
                                minHAT = hat;

                            if ( elevations[i] > maxTerrainZ )
                                maxTerrainZ = elevations[i];
                            if ( elevations[i] < minTerrainZ )
                                minTerrainZ = elevations[i];
                        }
                    }
                }
                else // per-centroid
                {
                    osgEarth::Bounds bounds = geom->getBounds();
                    const osg::Vec2d& center = bounds.center2d();
                    GeoPoint centroid(featureSRS, center.x(), center.y());
                    double   centroidElevation;

                    if ( eq.getElevation( centroid, centroidElevation, _maxRes ) )
                    {
                        for( unsigned i=0; i<geom->size(); ++i )
                        {
                            osg::Vec3d& p = (*geom)[i];
                            p.z() *= scaleZ;
                            p.z() += offsetZ;

                            double hat = p.z();
                            p.z() = centroidElevation + p.z();

                            // if necessary, convert the Z value (which is now in the map's SRS) back to
                            // the feature's SRS.
                            if ( !vertEquiv )
                            {
                                featureSRSwithMapVertDatum->transform(p, featureSRS, p);
                            }

                            if ( hat > maxHAT )
                                maxHAT = hat;
                            if ( hat < minHAT )
                                minHAT = hat;
                        }

                        if ( centroidElevation > maxTerrainZ )
                            maxTerrainZ = centroidElevation;
                        if ( centroidElevation < minTerrainZ )
                            minTerrainZ = centroidElevation;
                    }
                }
            }

            // Clamp - replace the geometry's Z with the terrain height.
            else // CLAMP_TO_TERRAIN
            {
                if ( perVertex )
                {
                    eq.getElevations( geom->asVector(), featureSRS, true, _maxRes );
                    
                    // if necessary, transform the Z values (which are now in the map SRS) back
                    // into the feature's SRS.
                    if ( !vertEquiv )
                    {
                        osg::ref_ptr<const SpatialReference> featureSRSwithMapVertDatum =
                            SpatialReference::create(featureSRS->getHorizInitString(), mapSRS->getVertInitString());

                        osg::Vec3d tempgeo;
                        for( unsigned i=0; i<geom->size(); ++i )
                        {
                            osg::Vec3d& p = (*geom)[i];
                            featureSRSwithMapVertDatum->transform(p, featureSRS, p);
                        }
                    }
                }
                else // per-centroid
                {
                    osgEarth::Bounds bounds = geom->getBounds();
                    const osg::Vec2d& center = bounds.center2d();
                    GeoPoint centroid(featureSRS, center.x(), center.y());
                    double   centroidElevation;

                    osg::ref_ptr<const SpatialReference> featureSRSWithMapVertDatum;
                    if ( !vertEquiv )
                        featureSRSWithMapVertDatum = SpatialReference::create(featureSRS->getHorizInitString(), mapSRS->getVertInitString());

                    if ( eq.getElevation( centroid, centroidElevation, _maxRes ) )
                    {
                        for( unsigned i=0; i<geom->size(); ++i )
                        {
                            osg::Vec3d& p = (*geom)[i];
                            p.z() = centroidElevation;
                            if ( !vertEquiv )
                            {
                                featureSRSWithMapVertDatum->transform(p, featureSRS, p);
                            }
                        }
                    }
                }
            }

            if ( !collectHATs )
            {
                for( Geometry::iterator i = geom->begin(); i != geom->end(); ++i )
                {
                    i->z() *= scaleZ;
                    i->z() += offsetZ;
                }
            }
        }

        if ( minHAT != DBL_MAX )
        {
            feature->set( "__min_hat", minHAT );
            feature->set( "__max_hat", maxHAT );
        }

        if ( minTerrainZ != DBL_MAX )
        {
            feature->set( "__min_terrain_z", minTerrainZ );
            feature->set( "__max_terrain_z", maxTerrainZ );
        }
    }
}
osg::Node*
BuildGeometryFilter::push( FeatureList& input, FilterContext& context )
{
    osg::ref_ptr<osg::Group> result = new osg::Group();

    computeLocalizers( context );

    const LineSymbol*    line  = _style.get<LineSymbol>();
    const PolygonSymbol* poly  = _style.get<PolygonSymbol>();
    const PointSymbol*   point = _style.get<PointSymbol>();

    // bin the feautres into polygons, lines, polygonized lines, and points.
    FeatureList polygons;
    FeatureList lines;
    FeatureList polygonizedLines;
    FeatureList points;

    for(FeatureList::iterator i = input.begin(); i != input.end(); ++i)
    {
        Feature* f = i->get();

        // first consider the overall style:
        bool has_polysymbol     = poly != 0L;
        bool has_linesymbol     = line != 0L && line->stroke()->widthUnits() == Units::PIXELS;
        bool has_polylinesymbol = line != 0L && line->stroke()->widthUnits() != Units::PIXELS;
        bool has_pointsymbol    = point != 0L;

        // if the featue has a style set, that overrides:
        if ( f->style().isSet() )
        {
            has_polysymbol     = has_polysymbol     || (f->style()->has<PolygonSymbol>());
            has_linesymbol     = has_linesymbol     || (f->style()->has<LineSymbol>() && f->style()->get<LineSymbol>()->stroke()->widthUnits() == Units::PIXELS);
            has_polylinesymbol = has_polylinesymbol || (f->style()->has<LineSymbol>() && f->style()->get<LineSymbol>()->stroke()->widthUnits() != Units::PIXELS);
            has_pointsymbol    = has_pointsymbol    || (f->style()->has<PointSymbol>());
        }

        // if no style is set, use the geometry type:
        if ( !has_polysymbol && !has_linesymbol && !has_polylinesymbol && !has_pointsymbol && f->getGeometry() )
        {
            switch( f->getGeometry()->getComponentType() )
            {
            case Geometry::TYPE_LINESTRING:
            case Geometry::TYPE_RING:
                f->style()->add( new LineSymbol() );
                has_linesymbol = true;
                break;

            case Geometry::TYPE_POINTSET:
                f->style()->add( new PointSymbol() );
                has_pointsymbol = true;
                break;

            case Geometry::TYPE_POLYGON:
                f->style()->add( new PolygonSymbol() );
                has_polysymbol = true;
                break;
            }
        }

        if ( has_polysymbol )
            polygons.push_back( f );

        if ( has_linesymbol )
            lines.push_back( f );

        if ( has_polylinesymbol )
            polygonizedLines.push_back( f );

        if ( has_pointsymbol )
            points.push_back( f );
    }

    // process them separately.

    if ( polygons.size() > 0 )
    {
        OE_TEST << LC << "Building " << polygons.size() << " polygons." << std::endl;
        osg::ref_ptr<osg::Geode> geode = processPolygons(polygons, context);
        if ( geode->getNumDrawables() > 0 )
        {
            osgUtil::Optimizer o;
            o.optimize( geode.get(), 
                osgUtil::Optimizer::MERGE_GEOMETRY |
                osgUtil::Optimizer::INDEX_MESH |
                osgUtil::Optimizer::VERTEX_PRETRANSFORM |
                osgUtil::Optimizer::VERTEX_POSTTRANSFORM );

            result->addChild( geode.get() );
        }
    }

    if ( polygonizedLines.size() > 0 )
    {
        OE_TEST << LC << "Building " << polygonizedLines.size() << " polygonized lines." << std::endl;
        bool twosided = polygons.size() > 0 ? false : true;
        osg::ref_ptr<osg::Geode> geode = processPolygonizedLines(polygonizedLines, twosided, context);
        if ( geode->getNumDrawables() > 0 )
        {
            osgUtil::Optimizer o;
            o.optimize( geode.get(), 
                osgUtil::Optimizer::MERGE_GEOMETRY |
                osgUtil::Optimizer::INDEX_MESH |
                osgUtil::Optimizer::VERTEX_PRETRANSFORM |
                osgUtil::Optimizer::VERTEX_POSTTRANSFORM );

            result->addChild( geode.get() );
        }
    }

    if ( lines.size() > 0 )
    {
        OE_TEST << LC << "Building " << lines.size() << " lines." << std::endl;
        osg::ref_ptr<osg::Geode> geode = processLines(lines, context);
        if ( geode->getNumDrawables() > 0 )
        {
            osgUtil::Optimizer o;
            o.optimize( geode.get(), 
                osgUtil::Optimizer::MERGE_GEOMETRY );

            applyLineSymbology( geode->getOrCreateStateSet(), line );
            result->addChild( geode.get() );
        }
    }

    if ( points.size() > 0 )
    {
        OE_TEST << LC << "Building " << points.size() << " points." << std::endl;
        osg::ref_ptr<osg::Geode> geode = processPoints(points, context);
        if ( geode->getNumDrawables() > 0 )
        {
            osgUtil::Optimizer o;
            o.optimize( geode.get(), 
                osgUtil::Optimizer::MERGE_GEOMETRY );

            applyPointSymbology( geode->getOrCreateStateSet(), point );
            result->addChild( geode.get() );
        }
    }

    // indicate that geometry contains clamping attributes
    if (_style.has<AltitudeSymbol>() &&
        _style.get<AltitudeSymbol>()->technique() == AltitudeSymbol::TECHNIQUE_GPU)
    {
        Clamping::installHasAttrsUniform( result->getOrCreateStateSet() );
    }    

    // Prepare buffer objects.
    AllocateAndMergeBufferObjectsVisitor allocAndMerge;
    result->accept( allocAndMerge );


    if ( result->getNumChildren() > 0 )
    {
        // apply the delocalization matrix for no-jitter
        return delocalize( result.release() );
    }
    else
    {
        return 0L;
    }
}
Example #20
0
void
FeatureNode::build()
{
    if ( !_clampCallback.valid() )
        _clampCallback = new ClampCallback(this);

    _attachPoint = 0L;

    // if there is existing geometry, kill it
    this->removeChildren( 0, this->getNumChildren() );

    if ( !getMapNode() )
        return;

    if ( _features.empty() )
        return;

    const Style &style = getStyle();

    // compilation options.
    GeometryCompilerOptions options = _options;

    // figure out what kind of altitude manipulation we need to perform.
    AnnotationUtils::AltitudePolicy ap;
    AnnotationUtils::getAltitudePolicy( style, ap );

    // If we're doing auto-clamping on the CPU, shut off compiler map clamping
    // clamping since it would be redundant.
    if ( ap.sceneClamping )
    {
        options.ignoreAltitudeSymbol() = true;
    }

    _clamperData.clear();

    osg::Node* node = _compiled.get();
    if (_needsRebuild || !_compiled.valid() )
    {
        // Clone the Features before rendering as the GeometryCompiler and it's filters can change the coordinates
        // of the geometry when performing localization or converting to geocentric.
        _extent = GeoExtent::INVALID;

        FeatureList clone;
        for(FeatureList::iterator itr = _features.begin(); itr != _features.end(); ++itr)
        {
            Feature* feature = new Feature( *itr->get(), osg::CopyOp::DEEP_COPY_ALL);
            GeoExtent featureExtent(feature->getSRS(), feature->getGeometry()->getBounds());

            if (_extent.isInvalid())
            {
                _extent = featureExtent;
            }
            else
            {
                _extent.expandToInclude( featureExtent );
            }
            clone.push_back( feature );
        }

        // prep the compiler:
        GeometryCompiler compiler( options );
        Session* session = new Session( getMapNode()->getMap(), _styleSheet.get() );

        FilterContext context( session, new FeatureProfile( _extent ), _extent, _index);

        _compiled = compiler.compile( clone, style, context );
        node = _compiled.get();
        _needsRebuild = false;

        // Compute the world bounds
        osg::BoundingSphered bounds;
        for( FeatureList::iterator itr = _features.begin(); itr != _features.end(); ++itr)
        {
            osg::BoundingSphered bs;
            itr->get()->getWorldBound(getMapNode()->getMapSRS(), bs);
            bounds.expandBy(bs);
        }

        // The polytope will ensure we only clamp to intersecting tiles:
        Feature::getWorldBoundingPolytope(bounds, getMapNode()->getMapSRS(), _featurePolytope);
    }

    if ( node )
    {
        if ( AnnotationUtils::styleRequiresAlphaBlending( style ) &&
             getStyle().get<ExtrusionSymbol>() )
        {
            node = AnnotationUtils::installTwoPassAlpha( node );
        }

        _attachPoint = new osg::Group();
        _attachPoint->addChild( node );

        // Draped (projected) geometry
        if ( ap.draping )
        {
            DrapeableNode* d = new DrapeableNode();
            d->addChild( _attachPoint );
            this->addChild( d );
        }

        // GPU-clamped geometry
        else if ( ap.gpuClamping )
        {
            ClampableNode* clampable = new ClampableNode();
            clampable->addChild( _attachPoint );
            this->addChild( clampable );
        }

        else
        {
            this->addChild( _attachPoint );

            // set default lighting based on whether we are extruding:
            setDefaultLighting( style.has<ExtrusionSymbol>() );
        }

        applyRenderSymbology(style);

        if ( getMapNode()->getTerrain() )
        {
            if ( ap.sceneClamping )
            {
                // Need dynamic data variance since scene clamping will change the verts
                SetDataVarianceVisitor sdv(osg::Object::DYNAMIC);
                this->accept(sdv);

                getMapNode()->getTerrain()->addTerrainCallback(_clampCallback.get());
                clamp(getMapNode()->getTerrain()->getGraph(), getMapNode()->getTerrain());
            }
            else
            {
                getMapNode()->getTerrain()->removeTerrainCallback( _clampCallback.get() );
            }
        }
    }
}
osg::Geode*
BuildGeometryFilter::processPolygonizedLines(FeatureList&   features, 
                                             bool           twosided,
                                             FilterContext& context)
{
    osg::Geode* geode = new osg::Geode();

    // establish some referencing
    bool                    makeECEF   = false;
    const SpatialReference* featureSRS = 0L;
    const SpatialReference* mapSRS     = 0L;

    if ( context.isGeoreferenced() )
    {
        makeECEF   = context.getSession()->getMapInfo().isGeocentric();
        featureSRS = context.extent()->getSRS();
        mapSRS     = context.getSession()->getMapInfo().getProfile()->getSRS();
    }

    // iterate over all features.
    for( FeatureList::iterator i = features.begin(); i != features.end(); ++i )
    {
        Feature* input = i->get();
        // extract the required line symbol; bail out if not found.
        const LineSymbol* line =
            input->style().isSet() && input->style()->has<LineSymbol>() ? input->style()->get<LineSymbol>() :
            _style.get<LineSymbol>();

        if ( !line )
            continue;

        // run a symbol script if present.
        if ( line->script().isSet() )
        {
            StringExpression temp( line->script().get() );
            input->eval( temp, &context );
        }

        // The operator we'll use to make lines into polygons.
        PolygonizeLinesOperator polygonizer( *line->stroke() );

        // iterate over all the feature's geometry parts. We will treat
        // them as lines strings.
        GeometryIterator parts( input->getGeometry(), true );
        while( parts.hasMore() )
        {
            Geometry* part = parts.next();

            // if the underlying geometry is a ring (or a polygon), close it so the
            // polygonizer will generate a closed loop.
            Ring* ring = dynamic_cast<Ring*>(part);
            if ( ring )
                ring->close();

            // skip invalid geometry
            if ( part->size() < 2 )
                continue;

            // transform the geometry into the target SRS and localize it about 
            // a local reference point.
            osg::ref_ptr<osg::Vec3Array> verts   = new osg::Vec3Array();
            osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array();
            transformAndLocalize( part->asVector(), featureSRS, verts.get(), normals.get(), mapSRS, _world2local, makeECEF );

            // turn the lines into polygons.
            osg::Geometry* geom = polygonizer( verts.get(), normals.get(), twosided );
            if ( geom )
            {
                geode->addDrawable( geom );
            }

            // record the geometry's primitive set(s) in the index:
            if ( context.featureIndex() )
                context.featureIndex()->tagDrawable( geom, input );
        
            // install clamping attributes if necessary
            if (_style.has<AltitudeSymbol>() &&
                _style.get<AltitudeSymbol>()->technique() == AltitudeSymbol::TECHNIQUE_GPU)
            {
                Clamping::applyDefaultClampingAttrs( geom, input->getDouble("__oe_verticalOffset", 0.0) );
            }
        }

        polygonizer.installShaders( geode );
    }
    return geode;
}
bool
ExtrudeGeometryFilter::process( FeatureList& features, FilterContext& context )
{
    // seed our random number generators
    Random wallSkinPRNG( _wallSkinSymbol.valid()? *_wallSkinSymbol->randomSeed() : 0, Random::METHOD_FAST );
    Random roofSkinPRNG( _roofSkinSymbol.valid()? *_roofSkinSymbol->randomSeed() : 0, Random::METHOD_FAST );

    for( FeatureList::iterator f = features.begin(); f != features.end(); ++f )
    {
        Feature* input = f->get();

        GeometryIterator iter( input->getGeometry(), false );
        while( iter.hasMore() )
        {
            Geometry* part = iter.next();

            osg::ref_ptr<osg::Geometry> walls = new osg::Geometry();
            walls->setUseVertexBufferObjects( _useVertexBufferObjects.get() );
            
            osg::ref_ptr<osg::Geometry> rooflines = 0L;
            osg::ref_ptr<osg::Geometry> baselines = 0L;
            osg::ref_ptr<osg::Geometry> outlines  = 0L;
            
            if ( part->getType() == Geometry::TYPE_POLYGON )
            {
                rooflines = new osg::Geometry();
                rooflines->setUseVertexBufferObjects( _useVertexBufferObjects.get() );

                // prep the shapes by making sure all polys are open:
                static_cast<Polygon*>(part)->open();
            }

            // fire up the outline geometry if we have a line symbol.
            if ( _outlineSymbol != 0L )
            {
                outlines = new osg::Geometry();
                outlines->setUseVertexBufferObjects( _useVertexBufferObjects.get() );
            }

            // make a base cap if we're doing stencil volumes.
            if ( _makeStencilVolume )
            {
                baselines = new osg::Geometry();
                baselines->setUseVertexBufferObjects( _useVertexBufferObjects.get() );
            }

            // calculate the extrusion height:
            float height;

            if ( _heightCallback.valid() )
            {
                height = _heightCallback->operator()(input, context);
            }
            else if ( _heightExpr.isSet() )
            {
                height = input->eval( _heightExpr.mutable_value(), &context );
            }
            else
            {
                height = *_extrusionSymbol->height();
            }

            // calculate the height offset from the base:
            float offset = 0.0;
            if ( _heightOffsetExpr.isSet() )
            {
                offset = input->eval( _heightOffsetExpr.mutable_value(), &context );
            }

            osg::ref_ptr<osg::StateSet> wallStateSet;
            osg::ref_ptr<osg::StateSet> roofStateSet;

            // calculate the wall texturing:
            SkinResource* wallSkin = 0L;
            if ( _wallSkinSymbol.valid() )
            {
                if ( _wallResLib.valid() )
                {
                    SkinSymbol querySymbol( *_wallSkinSymbol.get() );
                    querySymbol.objectHeight() = fabs(height) - offset;
                    wallSkin = _wallResLib->getSkin( &querySymbol, wallSkinPRNG, context.getDBOptions() );
                }

                else
                {
                    //TODO: simple single texture?
                }
            }

            // calculate the rooftop texture:
            SkinResource* roofSkin = 0L;
            if ( _roofSkinSymbol.valid() )
            {
                if ( _roofResLib.valid() )
                {
                    SkinSymbol querySymbol( *_roofSkinSymbol.get() );
                    roofSkin = _roofResLib->getSkin( &querySymbol, roofSkinPRNG, context.getDBOptions() );
                }

                else
                {
                    //TODO: simple single texture?
                }
            }

            // Build the data model for the structure.
            Structure structure;

            buildStructure(
                part, 
                height, 
                offset, 
                _extrusionSymbol->flatten().get(),
                wallSkin,
                roofSkin,
                structure,
                context);

            // Create the walls.
            if ( walls.valid() )
            {
                osg::Vec4f wallColor(1,1,1,1), wallBaseColor(1,1,1,1);

                if ( _wallPolygonSymbol.valid() )
                {
                    wallColor = _wallPolygonSymbol->fill()->color();
                }

                if ( _extrusionSymbol->wallGradientPercentage().isSet() )
                {
                    wallBaseColor = Color(wallColor).brightness( 1.0 - *_extrusionSymbol->wallGradientPercentage() );
                }
                else
                {
                    wallBaseColor = wallColor;
                }

                buildWallGeometry(structure, walls.get(), wallColor, wallBaseColor, wallSkin);

                if ( wallSkin )
                {
                    // Get a stateset for the individual wall stateset
                    context.resourceCache()->getOrCreateStateSet( wallSkin, wallStateSet );
                }
            }

            // tessellate and add the roofs if necessary:
            if ( rooflines.valid() )
            {
                osg::Vec4f roofColor(1,1,1,1);
                if ( _roofPolygonSymbol.valid() )
                {
                    roofColor = _roofPolygonSymbol->fill()->color();
                }

                buildRoofGeometry(structure, rooflines.get(), roofColor, roofSkin);

                if ( roofSkin )
                {
                    // Get a stateset for the individual roof skin
                    context.resourceCache()->getOrCreateStateSet( roofSkin, roofStateSet );
                }
            }

            if ( outlines.valid() )
            {
                osg::Vec4f outlineColor(1,1,1,1);
                if ( _outlineSymbol.valid() )
                {
                    outlineColor = _outlineSymbol->stroke()->color();
                }

                float minCreaseAngle = _outlineSymbol->creaseAngle().value();
                buildOutlineGeometry(structure, outlines.get(), outlineColor, minCreaseAngle);
            }

            if ( baselines.valid() )
            {
                //TODO.
                osgUtil::Tessellator tess;
                tess.setTessellationType( osgUtil::Tessellator::TESS_TYPE_GEOMETRY );
                tess.setWindingType( osgUtil::Tessellator::TESS_WINDING_ODD );
                tess.retessellatePolygons( *(baselines.get()) );
            }

            // Set up for feature naming and feature indexing:
            std::string name;
            if ( !_featureNameExpr.empty() )
                name = input->eval( _featureNameExpr, &context );

            FeatureSourceIndex* index = context.featureIndex();

            if ( walls.valid() )
            {
                addDrawable( walls.get(), wallStateSet.get(), name, input, index );
            }

            if ( rooflines.valid() )
            {
                addDrawable( rooflines.get(), roofStateSet.get(), name, input, index );
            }

            if ( baselines.valid() )
            {
                addDrawable( baselines.get(), 0L, name, input, index );
            }

            if ( outlines.valid() )
            {
                addDrawable( outlines.get(), 0L, name, input, index );
            }
        }
    }

    return true;
}
Example #23
0
FilterContext
ScatterFilter::push(FeatureList& features, const FilterContext& context )
{
    if ( !isSupported() ) {
        OE_WARN << LC << "support for this filter is not enabled" << std::endl;
        return context;
    }

    // seed the random number generator so the randomness is the same each time
    // todo: control this seeding based on the feature source name, perhaps?
    ::srand( _randomSeed );

    for( FeatureList::iterator i = features.begin(); i != features.end(); ++i )
    {
        Feature* f = i->get();
        
        Geometry* geom = f->getGeometry();
        if ( !geom )
            continue;

        const SpatialReference* geomSRS = context.profile()->getSRS();

        // first, undo the localization frame if there is one.
        context.toWorld( geom );

        // convert to geodetic if necessary, and compute the approximate area in sq km
        if ( context.isGeocentric() )
        {
            GeometryIterator gi( geom );
            while( gi.hasMore() )
                geomSRS->getGeographicSRS()->transformFromECEF( gi.next(), true );

            geomSRS = geomSRS->getGeographicSRS();
        }

        PointSet* points = new PointSet();

        if ( geom->getComponentType() == Geometry::TYPE_POLYGON )
        {
            polyScatter( geom, geomSRS, context, points );
        }
        else if (
            geom->getComponentType() == Geometry::TYPE_LINESTRING ||
            geom->getComponentType() == Geometry::TYPE_RING )            
        {
            lineScatter( geom, geomSRS, context, points );
        }
        else {
            OE_WARN << LC << "Sorry, don't know how to scatter a PointSet yet" << std::endl;
        }

        // convert back to geocentric if necessary.
        if ( context.isGeocentric() )
            context.profile()->getSRS()->getGeographicSRS()->transformToECEF( points, true );

        // re-apply the localization frame.
        context.toLocal( points );

        // replace the source geometry with the scattered points.
        f->setGeometry( points );
    }

    return context;
}