Пример #1
0
Config
FeatureSourceOptions::toConfig() const
{
    Config conf = DriverOptions::toConfig();

    //TODO: make each of these filters Configurable.
    for( FeatureFilterList::const_iterator i = _filters.begin(); i != _filters.end(); ++i )
    {
        BufferFilter* buffer = dynamic_cast<BufferFilter*>( i->get() );
        if ( buffer ) {
            Config bufferConf( "buffer" );
            bufferConf.addIfSet( "distance", buffer->distance() );
            conf.update( bufferConf );
        }
        ResampleFilter* resample = dynamic_cast<ResampleFilter*>( i->get() );
        if ( resample ) { 
            Config resampleConf( "resample" );
            resampleConf.addIfSet( "min_length", resample->minLength() );
            resampleConf.addIfSet( "max_length", resample->maxLength() );
            conf.update( resampleConf );
        }
    }

    return conf;
}
Пример #2
0
FeatureSourceOptions::FeatureSourceOptions( const PluginOptions* opt ) :
DriverOptions( opt )
{
    const Config& bufferConf = config().child("buffer");
    if ( !bufferConf.empty() )
    {
        BufferFilter* buffer = new BufferFilter();
        bufferConf.getIfSet( "distance", buffer->distance() );
        _filters.push_back( buffer );
    }

    // resample operation:
    // resampling must occur AFTER buffering, because the buffer op will remove colinear segments.
    const Config& resampleConf = config().child("resample");
    if ( !resampleConf.empty() )
    {
        ResampleFilter* resample = new ResampleFilter();
        resampleConf.getIfSet( "min_length", resample->minLength() );
        resampleConf.getIfSet( "max_length", resample->maxLength() );
        _filters.push_back( resample );
    }
}
Пример #3
0
osg::Node*
Graticule::createGridLevel( unsigned int levelNum ) const
{
    if ( !_map->isGeocentric() )
    {
        OE_WARN << "Graticule: only supports geocentric maps" << std::endl;
        return 0L;
    }

    Graticule::Level level;
    if ( !getLevel( levelNum, level ) )
        return 0L;

    OE_DEBUG << "Graticule: creating grid level " << levelNum << std::endl;

    osg::Group* group = new osg::Group();

    const Profile* mapProfile = _map->getProfile();
    const GeoExtent& pex = mapProfile->getExtent();

    double tw = pex.width() / (double)level._cellsX;
    double th = pex.height() / (double)level._cellsY;

    for( unsigned int x=0; x<level._cellsX; ++x )
    {
        for( unsigned int y=0; y<level._cellsY; ++y )
        {
            GeoExtent tex(
                mapProfile->getSRS(),
                pex.xMin() + tw * (double)x,
                pex.yMin() + th * (double)y,
                pex.xMin() + tw * (double)(x+1),
                pex.yMin() + th * (double)(y+1) );

            Geometry* geom = createCellGeometry( tex, level._lineWidth, pex, _map->isGeocentric() );

            Feature* feature = new Feature();
            feature->setGeometry( geom );
            FeatureList features;
            features.push_back( feature );

            FilterContext cx;
            cx.profile() = new FeatureProfile( tex );
            //cx.isGeocentric() = _map->isGeocentric();

            if ( _map->isGeocentric() )
            {
                // We need to make sure that on a round globe, the points are sampled such that
                // long segments follow the curvature of the earth.
                ResampleFilter resample;
                resample.maxLength() = tex.width() / 10.0;
                cx = resample.push( features, cx );
            }

            TransformFilter xform( mapProfile->getSRS() );
            //xform.setMakeGeocentric( _map->isGeocentric() );
            //xform.setLocalizeCoordinates( true );
            cx = xform.push( features, cx );

            osg::ref_ptr<osg::Node> output;
            BuildGeometryFilter bg;
            bg.setStyle( _lineStyle );
            //cx = bg.push( features, cx );
            output = bg.push( features, cx ); //.getNode();

            if ( _map->isGeocentric() )
            {
                // get the geocentric control point:
                double cplon, cplat, cpx, cpy, cpz;
                tex.getCentroid( cplon, cplat );
                tex.getSRS()->getEllipsoid()->convertLatLongHeightToXYZ(
                    osg::DegreesToRadians( cplat ), osg::DegreesToRadians( cplon ), 0.0, cpx, cpy, cpz );
                osg::Vec3 controlPoint(cpx, cpy, cpz);

                // get the horizon point:
                tex.getSRS()->getEllipsoid()->convertLatLongHeightToXYZ(
                    osg::DegreesToRadians( tex.yMin() ), osg::DegreesToRadians( tex.xMin() ), 0.0,
                    cpx, cpy, cpz );
                osg::Vec3 horizonPoint(cpx, cpy, cpz);

                // the deviation is the dot product of the control vector and the vector from the
                // control point to the horizon point.
                osg::Vec3 controlPointNorm = controlPoint; controlPointNorm.normalize();
                osg::Vec3 horizonVecNorm = horizonPoint - controlPoint; horizonVecNorm.normalize();                
                float deviation = controlPointNorm * horizonVecNorm;

                // construct the culling callback using the deviation.
                osg::ClusterCullingCallback* ccc = new osg::ClusterCullingCallback();
                ccc->set( controlPoint, controlPointNorm, deviation, (controlPoint-horizonPoint).length() );

                // need a new group, because never put a cluster culler on a matrixtransform (doesn't work)
                osg::Group* me = new osg::Group();
                me->setCullCallback( ccc );
                me->addChild( output.get() );
                output = me;
            }

            group->addChild( output.get() );
        }
    }

    // organize it for better culling
    osgUtil::Optimizer opt;
    opt.optimize( group, osgUtil::Optimizer::SPATIALIZE_GROUPS );

    osg::Node* result = group;

    if ( levelNum < getNumLevels() )
    {
        Graticule::Level nextLevel;
        if ( getLevel( levelNum+1, nextLevel ) )
        {
            osg::PagedLOD* plod = new osg::PagedLOD();
            plod->addChild( group, nextLevel._maxRange, level._maxRange );
            std::stringstream buf;
            buf << levelNum+1 << "_" << getID() << "." << GRID_MARKER << "." << GRATICLE_EXTENSION;
            std::string bufStr = buf.str();
            plod->setFileName( 1, bufStr );
            plod->setRange( 1, 0, nextLevel._maxRange );
            result = plod;
        }
    }

    return result;
}
osg::Node*
GeometryCompiler::compile(FeatureList&          workingSet,
                          const Style&          style,
                          const FilterContext&  context)
{
#ifdef PROFILING
    osg::Timer_t p_start = osg::Timer::instance()->tick();
    unsigned p_features = workingSet.size();
#endif

    // for debugging/validation.
    std::vector<std::string> history;
    bool trackHistory = (_options.validate() == true);

    osg::ref_ptr<osg::Group> resultGroup = new osg::Group();

    // create a filter context that will track feature data through the process
    FilterContext sharedCX = context;

    if ( !sharedCX.extent().isSet() && sharedCX.profile() )
    {
        sharedCX.extent() = sharedCX.profile()->getExtent();
    }

    // ref_ptr's to hold defaults in case we need them.
    osg::ref_ptr<PointSymbol>   defaultPoint;
    osg::ref_ptr<LineSymbol>    defaultLine;
    osg::ref_ptr<PolygonSymbol> defaultPolygon;

    // go through the Style and figure out which filters to use.
    const PointSymbol*     point     = style.get<PointSymbol>();
    const LineSymbol*      line      = style.get<LineSymbol>();
    const PolygonSymbol*   polygon   = style.get<PolygonSymbol>();
    const ExtrusionSymbol* extrusion = style.get<ExtrusionSymbol>();
    const AltitudeSymbol*  altitude  = style.get<AltitudeSymbol>();
    const TextSymbol*      text      = style.get<TextSymbol>();
    const MarkerSymbol*    marker    = style.get<MarkerSymbol>();    // to be deprecated
    const IconSymbol*      icon      = style.get<IconSymbol>();
    const ModelSymbol*     model     = style.get<ModelSymbol>();

    // Perform tessellation first.
    if ( line )
    {
        if ( line->tessellation().isSet() )
        {
            TemplateFeatureFilter<TessellateOperator> filter;
            filter.setNumPartitions( *line->tessellation() );
            filter.setDefaultGeoInterp( _options.geoInterp().get() );
            sharedCX = filter.push( workingSet, sharedCX );
            if ( trackHistory ) history.push_back( "tessellation" );
        }
        else if ( line->tessellationSize().isSet() )
        {
            TemplateFeatureFilter<TessellateOperator> filter;
            filter.setMaxPartitionSize( *line->tessellationSize() );
            filter.setDefaultGeoInterp( _options.geoInterp().get() );
            sharedCX = filter.push( workingSet, sharedCX );
            if ( trackHistory ) history.push_back( "tessellationSize" );
        }
    }

    // if the style was empty, use some defaults based on the geometry type of the
    // first feature.
    if ( !point && !line && !polygon && !marker && !extrusion && !text && !model && !icon && workingSet.size() > 0 )
    {
        Feature* first = workingSet.begin()->get();
        Geometry* geom = first->getGeometry();
        if ( geom )
        {
            switch( geom->getComponentType() )
            {
            case Geometry::TYPE_LINESTRING:
            case Geometry::TYPE_RING:
                defaultLine = new LineSymbol();
                line = defaultLine.get();
                break;
            case Geometry::TYPE_POINTSET:
                defaultPoint = new PointSymbol();
                point = defaultPoint.get();
                break;
            case Geometry::TYPE_POLYGON:
                defaultPolygon = new PolygonSymbol();
                polygon = defaultPolygon.get();
                break;
            case Geometry::TYPE_MULTI:
            case Geometry::TYPE_UNKNOWN:
                break;
            }
        }
    }

    // resample the geometry if necessary:
    if (_options.resampleMode().isSet())
    {
        ResampleFilter resample;
        resample.resampleMode() = *_options.resampleMode();        
        if (_options.resampleMaxLength().isSet())
        {
            resample.maxLength() = *_options.resampleMaxLength();
        }                   
        sharedCX = resample.push( workingSet, sharedCX ); 
        if ( trackHistory ) history.push_back( "resample" );
    }    
    
    // check whether we need to do elevation clamping:
    bool altRequired =
        _options.ignoreAltitudeSymbol() != true &&
        altitude && (
            altitude->clamping() != AltitudeSymbol::CLAMP_NONE ||
            altitude->verticalOffset().isSet() ||
            altitude->verticalScale().isSet() ||
            altitude->script().isSet() );

    // marker substitution -- to be deprecated in favor of model/icon
    if ( marker )
    {
        if ( trackHistory ) history.push_back( "marker" );

        // use a separate filter context since we'll be munging the data
        FilterContext markerCX = sharedCX;

        if ( marker->placement() == MarkerSymbol::PLACEMENT_RANDOM   ||
             marker->placement() == MarkerSymbol::PLACEMENT_INTERVAL )
        {
            ScatterFilter scatter;
            scatter.setDensity( *marker->density() );
            scatter.setRandom( marker->placement() == MarkerSymbol::PLACEMENT_RANDOM );
            scatter.setRandomSeed( *marker->randomSeed() );
            markerCX = scatter.push( workingSet, markerCX );
            if ( trackHistory ) history.push_back( "scatter" );
        }
        else if ( marker->placement() == MarkerSymbol::PLACEMENT_CENTROID )
        {
            CentroidFilter centroid;
            markerCX = centroid.push( workingSet, markerCX );  
            if ( trackHistory ) history.push_back( "centroid" );
        }

        if ( altRequired )
        {
            AltitudeFilter clamp;
            clamp.setPropertiesFromStyle( style );
            markerCX = clamp.push( workingSet, markerCX );
            if ( trackHistory ) history.push_back( "altitude" );

            // don't set this; we changed the input data.
            //altRequired = false;
        }

        SubstituteModelFilter sub( style );

        sub.setClustering( *_options.clustering() );

        sub.setUseDrawInstanced( *_options.instancing() );

        if ( _options.featureName().isSet() )
            sub.setFeatureNameExpr( *_options.featureName() );

        osg::Node* node = sub.push( workingSet, markerCX );
        if ( node )
        {
            if ( trackHistory ) history.push_back( "substitute" );
            resultGroup->addChild( node );
        }
    }

    // instance substitution (replaces marker)
    else if ( model )
    {
        const InstanceSymbol* instance = model ? (const InstanceSymbol*)model : (const InstanceSymbol*)icon;

        // use a separate filter context since we'll be munging the data
        FilterContext localCX = sharedCX;
        
        if ( trackHistory ) history.push_back( "model");

        if ( instance->placement() == InstanceSymbol::PLACEMENT_RANDOM   ||
             instance->placement() == InstanceSymbol::PLACEMENT_INTERVAL )
        {
            ScatterFilter scatter;
            scatter.setDensity( *instance->density() );
            scatter.setRandom( instance->placement() == InstanceSymbol::PLACEMENT_RANDOM );
            scatter.setRandomSeed( *instance->randomSeed() );
            localCX = scatter.push( workingSet, localCX );
            if ( trackHistory ) history.push_back( "scatter" );
        }
        else if ( instance->placement() == InstanceSymbol::PLACEMENT_CENTROID )
        {
            CentroidFilter centroid;
            localCX = centroid.push( workingSet, localCX );
            if ( trackHistory ) history.push_back( "centroid" );
        }

        if ( altRequired )
        {
            AltitudeFilter clamp;
            clamp.setPropertiesFromStyle( style );
            localCX = clamp.push( workingSet, localCX );
            if ( trackHistory ) history.push_back( "altitude" );
        }

        SubstituteModelFilter sub( style );

        // activate clustering
        sub.setClustering( *_options.clustering() );

        // activate draw-instancing
        sub.setUseDrawInstanced( *_options.instancing() );

        // activate feature naming
        if ( _options.featureName().isSet() )
            sub.setFeatureNameExpr( *_options.featureName() );
        

        osg::Node* node = sub.push( workingSet, localCX );
        if ( node )
        {
            if ( trackHistory ) history.push_back( "substitute" );

            resultGroup->addChild( node );

            // enable auto scaling on the group?
            if ( model && model->autoScale() == true )
            {
                resultGroup->getOrCreateStateSet()->setRenderBinDetails(0, osgEarth::AUTO_SCALE_BIN );
            }
        }
    }

    // extruded geometry
    if ( extrusion )
    {
        if ( altRequired )
        {
            AltitudeFilter clamp;
            clamp.setPropertiesFromStyle( style );
            sharedCX = clamp.push( workingSet, sharedCX );
            if ( trackHistory ) history.push_back( "altitude" );
            altRequired = false;
        }

        ExtrudeGeometryFilter extrude;
        extrude.setStyle( style );

        // apply per-feature naming if requested.
        if ( _options.featureName().isSet() )
            extrude.setFeatureNameExpr( *_options.featureName() );

        if ( _options.mergeGeometry().isSet() )
            extrude.setMergeGeometry( *_options.mergeGeometry() );

        osg::Node* node = extrude.push( workingSet, sharedCX );
        if ( node )
        {
            if ( trackHistory ) history.push_back( "extrude" );
            resultGroup->addChild( node );
        }
        
    }

    // simple geometry
    else if ( point || line || polygon )
    {
        if ( altRequired )
        {
            AltitudeFilter clamp;
            clamp.setPropertiesFromStyle( style );
            sharedCX = clamp.push( workingSet, sharedCX );
            if ( trackHistory ) history.push_back( "altitude" );
            altRequired = false;
        }

        BuildGeometryFilter filter( style );
        filter.maxGranularity() = *_options.maxGranularity();
        filter.geoInterp()      = *_options.geoInterp();

        if ( _options.featureName().isSet() )
            filter.featureName() = *_options.featureName();

        osg::Node* node = filter.push( workingSet, sharedCX );
        if ( node )
        {
            if ( trackHistory ) history.push_back( "geometry" );
            resultGroup->addChild( node );
        }
    }

    if ( text || icon )
    {
        if ( altRequired )
        {
            AltitudeFilter clamp;
            clamp.setPropertiesFromStyle( style );
            sharedCX = clamp.push( workingSet, sharedCX );
            if ( trackHistory ) history.push_back( "altitude" );
            altRequired = false;
        }

        BuildTextFilter filter( style );
        osg::Node* node = filter.push( workingSet, sharedCX );
        if ( node )
        {
            if ( trackHistory ) history.push_back( "text" );
            resultGroup->addChild( node );
        }
    }

    if (Registry::capabilities().supportsGLSL())
    {
        if ( _options.shaderPolicy() == SHADERPOLICY_GENERATE )
        {
            // no ss cache because we will optimize later.
            Registry::shaderGenerator().run( 
                resultGroup.get(),
                "osgEarth.GeomCompiler" );
        }
        else if ( _options.shaderPolicy() == SHADERPOLICY_DISABLE )
        {
            resultGroup->getOrCreateStateSet()->setAttributeAndModes(
                new osg::Program(),
                osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE );
        
            if ( trackHistory ) history.push_back( "no shaders" );
        }
    }

    // Optimize stateset sharing.
    if ( _options.optimizeStateSharing() == true )
    {
        // Common state set cache?
        osg::ref_ptr<StateSetCache> sscache;
        if ( sharedCX.getSession() )
        {
            // with a shared cache, don't combine statesets. They may be
            // in the live graph
            sscache = sharedCX.getSession()->getStateSetCache();
            sscache->consolidateStateAttributes( resultGroup.get() );
        }
        else 
        {
            // isolated: perform full optimization
            sscache = new StateSetCache();
            sscache->optimize( resultGroup.get() );
        }
        
        if ( trackHistory ) history.push_back( "share state" );
    }

    if ( _options.optimize() == true )
    {
        OE_DEBUG << LC << "optimize begin" << std::endl;

        // Run the optimizer on the resulting graph
        int optimizations =
            osgUtil::Optimizer::FLATTEN_STATIC_TRANSFORMS |
            osgUtil::Optimizer::REMOVE_REDUNDANT_NODES |
            osgUtil::Optimizer::COMBINE_ADJACENT_LODS |
            osgUtil::Optimizer::SHARE_DUPLICATE_STATE |
            osgUtil::Optimizer::MERGE_GEOMETRY |
            osgUtil::Optimizer::CHECK_GEOMETRY |
            osgUtil::Optimizer::MERGE_GEODES |
            osgUtil::Optimizer::STATIC_OBJECT_DETECTION;

        osgUtil::Optimizer opt;
        opt.optimize(resultGroup.get(), optimizations);
        OE_DEBUG << LC << "optimize complete" << std::endl;

        if ( trackHistory ) history.push_back( "optimize" );
    }
    

    //test: dump the tile to disk
    //osgDB::writeNodeFile( *(resultGroup.get()), "out.osg" );

#ifdef PROFILING
    static double totalTime = 0.0;
    static Threading::Mutex totalTimeMutex;
    osg::Timer_t p_end = osg::Timer::instance()->tick();
    double t = osg::Timer::instance()->delta_s(p_start, p_end);
    totalTimeMutex.lock();
    totalTime += t;
    totalTimeMutex.unlock();
    OE_INFO << LC
        << "features = " << p_features
        << ", time = " << t << " s.  cummulative = " 
        << totalTime << " s."
        << std::endl;
#endif


    if ( _options.validate() == true )
    {
        OE_NOTICE << LC << "-- Start Debugging --\n";
        std::stringstream buf;
        buf << "HISTORY ";
        for(std::vector<std::string>::iterator h = history.begin(); h != history.end(); ++h)
            buf << ".. " << *h;
        OE_NOTICE << LC << buf.str() << "\n";
        osgEarth::GeometryValidator validator;
        resultGroup->accept(validator);
        OE_NOTICE << LC << "-- End Debugging --\n";
    }

    return resultGroup.release();
}
Пример #5
0
osg::Node*
GeometryCompiler::compile(FeatureList&          workingSet,
                          const Style&          style,
                          const FilterContext&  context)
{
    osg::ref_ptr<osg::Group> resultGroup = new osg::Group();

    // create a filter context that will track feature data through the process
    FilterContext sharedCX = context;
    if ( !sharedCX.extent().isSet() )
        sharedCX.extent() = sharedCX.profile()->getExtent();

    // only localize coordinates if the map is geocentric AND the extent is
    // less than 180 degrees.
    const MapInfo& mi = sharedCX.getSession()->getMapInfo();
    GeoExtent workingExtent = sharedCX.extent()->transform( sharedCX.profile()->getSRS()->getGeographicSRS() );
    bool localize = mi.isGeocentric() && workingExtent.width() < 180.0;

    // go through the Style and figure out which filters to use.
    const MarkerSymbol*    marker    = style.get<MarkerSymbol>();
    const PointSymbol*     point     = style.get<PointSymbol>();
    const LineSymbol*      line      = style.get<LineSymbol>();
    const PolygonSymbol*   polygon   = style.get<PolygonSymbol>();
    const ExtrusionSymbol* extrusion = style.get<ExtrusionSymbol>();
    const AltitudeSymbol*  altitude  = style.get<AltitudeSymbol>();
    const TextSymbol*      text      = style.get<TextSymbol>();

    if (_options.resampleMode().isSet())
    {
        ResampleFilter resample;
        resample.resampleMode() = *_options.resampleMode();        
        if (_options.resampleMaxLength().isSet())
        {
            resample.maxLength() = *_options.resampleMaxLength();
        }                   
        sharedCX = resample.push( workingSet, sharedCX );        
    }    
    
    bool clampRequired =
        altitude && altitude->clamping() != AltitudeSymbol::CLAMP_NONE;
    
    // transform the features into the map profile
    TransformFilter xform( mi.getProfile()->getSRS(), mi.isGeocentric() );   
    xform.setLocalizeCoordinates( localize );
    if ( altitude && altitude->verticalOffset().isSet() && !clampRequired )
        xform.setMatrix( osg::Matrixd::translate(0, 0, *altitude->verticalOffset()) );
    sharedCX = xform.push( workingSet, sharedCX );

    // model substitution
    if ( marker )
    {
        // use a separate filter context since we'll be munging the data
        FilterContext markerCX = sharedCX;

        if ( marker->placement() == MarkerSymbol::PLACEMENT_RANDOM   ||
             marker->placement() == MarkerSymbol::PLACEMENT_INTERVAL )
        {
            ScatterFilter scatter;
            scatter.setDensity( *marker->density() );
            scatter.setRandom( marker->placement() == MarkerSymbol::PLACEMENT_RANDOM );
            scatter.setRandomSeed( *marker->randomSeed() );
            markerCX = scatter.push( workingSet, markerCX );
        }

        if ( clampRequired )
        {
            ClampFilter clamp;
            clamp.setIgnoreZ( altitude->clamping() == AltitudeSymbol::CLAMP_TO_TERRAIN );
            clamp.setOffsetZ( *altitude->verticalOffset() );
            markerCX = clamp.push( workingSet, markerCX );

            // don't set this; we changed the input data.
            //clampRequired = false;
        }

        SubstituteModelFilter sub( style );
        sub.setClustering( *_options.clustering() );
        if ( marker->scale().isSet() )
            sub.setModelMatrix( osg::Matrixd::scale( *marker->scale() ) );
        if ( _options.featureName().isSet() )
            sub.setFeatureNameExpr( *_options.featureName() );

        markerCX = sub.push( workingSet, markerCX );

        osg::Node* node = sub.getNode();
        if ( node )
        {
            if ( markerCX.hasReferenceFrame() )
            {
                osg::MatrixTransform* delocalizer = new osg::MatrixTransform( markerCX.inverseReferenceFrame() );
                delocalizer->addChild( node );
                node = delocalizer;
            }

            resultGroup->addChild( node );
        }
    }

    // extruded geometry
    if ( extrusion && ( line || polygon ) )
    {
        if ( clampRequired )
        {
            ClampFilter clamp;
            clamp.setIgnoreZ( altitude->clamping() == AltitudeSymbol::CLAMP_TO_TERRAIN );
            if ( extrusion->heightReference() == ExtrusionSymbol::HEIGHT_REFERENCE_MSL )
                clamp.setMaxZAttributeName( "__max_z");
            clamp.setOffsetZ( *altitude->verticalOffset() );
            sharedCX = clamp.push( workingSet, sharedCX );
            clampRequired = false;
        }

        ExtrudeGeometryFilter extrude;
        if ( extrusion )
        {
            if ( extrusion->height().isSet() )
                extrude.setExtrusionHeight( *extrusion->height() );
            if ( extrusion->heightExpression().isSet() )
                extrude.setExtrusionExpr( *extrusion->heightExpression() );
            if ( extrusion->heightReference() == ExtrusionSymbol::HEIGHT_REFERENCE_MSL )
                extrude.setHeightOffsetExpression( NumericExpression("[__max_z]") );
            if ( _options.featureName().isSet() )
                extrude.setFeatureNameExpr( *_options.featureName() );
            extrude.setFlatten( *extrusion->flatten() );
        }
        if ( polygon )
        {
            extrude.setColor( polygon->fill()->color() );
        }

        osg::Node* node = extrude.push( workingSet, sharedCX );
        if ( node )
        {
            if ( sharedCX.hasReferenceFrame() )
            {
                osg::MatrixTransform* delocalizer = new osg::MatrixTransform( sharedCX.inverseReferenceFrame() );
                delocalizer->addChild( node );
                node = delocalizer;
            }
            resultGroup->addChild( node );
        }
    }

    // simple geometry
    else if ( point || line || polygon )
    {
        if ( clampRequired )
        {
            ClampFilter clamp;
            clamp.setIgnoreZ( altitude->clamping() == AltitudeSymbol::CLAMP_TO_TERRAIN );
            clamp.setOffsetZ( *altitude->verticalOffset() );
            sharedCX = clamp.push( workingSet, sharedCX );
            clampRequired = false;
        }

        BuildGeometryFilter filter( style );
        if ( _options.maxGranularity().isSet() )
            filter.maxGranularity() = *_options.maxGranularity();
        if ( _options.geoInterp().isSet() )
            filter.geoInterp() = *_options.geoInterp();
        if ( _options.mergeGeometry().isSet() )
            filter.mergeGeometry() = *_options.mergeGeometry();
        if ( _options.featureName().isSet() )
            filter.featureName() = *_options.featureName();
        sharedCX = filter.push( workingSet, sharedCX );

        osg::Node* node = filter.getNode();
        if ( node )
        {
            if ( sharedCX.hasReferenceFrame() )
            {
                osg::MatrixTransform* delocalizer = new osg::MatrixTransform( sharedCX.inverseReferenceFrame() );
                delocalizer->addChild( node );
                node = delocalizer;
            }
            resultGroup->addChild( node );
        }
    }

    if ( text )
    {
        if ( clampRequired )
        {
            ClampFilter clamp;
            clamp.setIgnoreZ( altitude->clamping() == AltitudeSymbol::CLAMP_TO_TERRAIN );
            clamp.setOffsetZ( *altitude->verticalOffset() );
            sharedCX = clamp.push( workingSet, sharedCX );
            clampRequired = false;
        }

        BuildTextFilter filter( style );
        sharedCX = filter.push( workingSet, sharedCX );

        osg::Node* node = filter.takeNode();
        if ( node )
        {
            if ( sharedCX.hasReferenceFrame() )
            {
                osg::MatrixTransform* delocalizer = new osg::MatrixTransform( sharedCX.inverseReferenceFrame() );
                delocalizer->addChild( node );
                node = delocalizer;
            }
            resultGroup->addChild( node );
        }
    }

    //else // insufficient symbology
    //{
    //    OE_WARN << LC << "Insufficient symbology; no geometry created" << std::endl;
    //}

#if 0
    // install the localization transform if necessary.
    if ( cx.hasReferenceFrame() )
    {
        osg::MatrixTransform* delocalizer = new osg::MatrixTransform( cx.inverseReferenceFrame() );
        delocalizer->addChild( resultGroup.get() );
        resultGroup = delocalizer;
    }
#endif

    resultGroup->getOrCreateStateSet()->setMode( GL_BLEND, 1 );

    //osgDB::writeNodeFile( *(resultGroup.get()), "out.osg" );

    return resultGroup.release();
}
    //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;
    }
Пример #7
0
osg::Node*
GeometryCompiler::compile(FeatureList&          workingSet,
                          const Style&          style,
                          const FilterContext&  context)
{
#ifdef PROFILING
    osg::Timer_t p_start = osg::Timer::instance()->tick();
    unsigned p_features = workingSet.size();
#endif

    osg::ref_ptr<osg::Group> resultGroup = new osg::Group();

    // create a filter context that will track feature data through the process
    FilterContext sharedCX = context;
    if ( !sharedCX.extent().isSet() && sharedCX.profile() )
    {
        sharedCX.extent() = sharedCX.profile()->getExtent();
    }

    // ref_ptr's to hold defaults in case we need them.
    osg::ref_ptr<PointSymbol>   defaultPoint;
    osg::ref_ptr<LineSymbol>    defaultLine;
    osg::ref_ptr<PolygonSymbol> defaultPolygon;

    // go through the Style and figure out which filters to use.
    const PointSymbol*     point     = style.get<PointSymbol>();
    const LineSymbol*      line      = style.get<LineSymbol>();
    const PolygonSymbol*   polygon   = style.get<PolygonSymbol>();
    const ExtrusionSymbol* extrusion = style.get<ExtrusionSymbol>();
    const AltitudeSymbol*  altitude  = style.get<AltitudeSymbol>();
    const TextSymbol*      text      = style.get<TextSymbol>();
    const MarkerSymbol*    marker    = style.get<MarkerSymbol>();    // to be deprecated
    const IconSymbol*      icon      = style.get<IconSymbol>();
    const ModelSymbol*     model     = style.get<ModelSymbol>();

    // check whether we need tessellation:
    if ( line && line->tessellation().isSet() )
    {
        TemplateFeatureFilter<TessellateOperator> filter;
        filter.setNumPartitions( *line->tessellation() );
        sharedCX = filter.push( workingSet, sharedCX );
    }

    // if the style was empty, use some defaults based on the geometry type of the
    // first feature.
    if ( !point && !line && !polygon && !marker && !extrusion && !text && !model && !icon && workingSet.size() > 0 )
    {
        Feature* first = workingSet.begin()->get();
        Geometry* geom = first->getGeometry();
        if ( geom )
        {
            switch( geom->getComponentType() )
            {
            case Geometry::TYPE_LINESTRING:
            case Geometry::TYPE_RING:
                defaultLine = new LineSymbol();
                line = defaultLine.get();
                break;
            case Geometry::TYPE_POINTSET:
                defaultPoint = new PointSymbol();
                point = defaultPoint.get();
                break;
            case Geometry::TYPE_POLYGON:
                defaultPolygon = new PolygonSymbol();
                polygon = defaultPolygon.get();
                break;
            case Geometry::TYPE_MULTI:
            case Geometry::TYPE_UNKNOWN:
                break;
            }
        }
    }

    // resample the geometry if necessary:
    if (_options.resampleMode().isSet())
    {
        ResampleFilter resample;
        resample.resampleMode() = *_options.resampleMode();        
        if (_options.resampleMaxLength().isSet())
        {
            resample.maxLength() = *_options.resampleMaxLength();
        }                   
        sharedCX = resample.push( workingSet, sharedCX );        
    }    
    
    // check whether we need to do elevation clamping:
    bool altRequired =
        _options.ignoreAltitudeSymbol() != true &&
        altitude && (
            altitude->clamping() != AltitudeSymbol::CLAMP_NONE ||
            altitude->verticalOffset().isSet() ||
            altitude->verticalScale().isSet() ||
            altitude->script().isSet() );

    // marker substitution -- to be deprecated in favor of model/icon
    if ( marker )
    {
        // use a separate filter context since we'll be munging the data
        FilterContext markerCX = sharedCX;

        if ( marker->placement() == MarkerSymbol::PLACEMENT_RANDOM   ||
             marker->placement() == MarkerSymbol::PLACEMENT_INTERVAL )
        {
            ScatterFilter scatter;
            scatter.setDensity( *marker->density() );
            scatter.setRandom( marker->placement() == MarkerSymbol::PLACEMENT_RANDOM );
            scatter.setRandomSeed( *marker->randomSeed() );
            markerCX = scatter.push( workingSet, markerCX );
        }
        else if ( marker->placement() == MarkerSymbol::PLACEMENT_CENTROID )
        {
            CentroidFilter centroid;
            centroid.push( workingSet, markerCX );
        }

        if ( altRequired )
        {
            AltitudeFilter clamp;
            clamp.setPropertiesFromStyle( style );
            markerCX = clamp.push( workingSet, markerCX );

            // don't set this; we changed the input data.
            //altRequired = false;
        }

        SubstituteModelFilter sub( style );

        sub.setClustering( *_options.clustering() );

        sub.setUseDrawInstanced( *_options.instancing() );

        if ( _options.featureName().isSet() )
            sub.setFeatureNameExpr( *_options.featureName() );

        osg::Node* node = sub.push( workingSet, markerCX );
        if ( node )
        {
            resultGroup->addChild( node );
        }
    }

    // instance substitution (replaces marker)
    else if ( model )
    {
        const InstanceSymbol* instance = model ? (const InstanceSymbol*)model : (const InstanceSymbol*)icon;

        // use a separate filter context since we'll be munging the data
        FilterContext localCX = sharedCX;

        if ( instance->placement() == InstanceSymbol::PLACEMENT_RANDOM   ||
             instance->placement() == InstanceSymbol::PLACEMENT_INTERVAL )
        {
            ScatterFilter scatter;
            scatter.setDensity( *instance->density() );
            scatter.setRandom( instance->placement() == InstanceSymbol::PLACEMENT_RANDOM );
            scatter.setRandomSeed( *instance->randomSeed() );
            localCX = scatter.push( workingSet, localCX );
        }
        else if ( instance->placement() == InstanceSymbol::PLACEMENT_CENTROID )
        {
            CentroidFilter centroid;
            centroid.push( workingSet, localCX );
        }

        if ( altRequired )
        {
            AltitudeFilter clamp;
            clamp.setPropertiesFromStyle( style );
            localCX = clamp.push( workingSet, localCX );
        }

        SubstituteModelFilter sub( style );

        // activate clustering
        sub.setClustering( *_options.clustering() );

        // activate draw-instancing
        sub.setUseDrawInstanced( *_options.instancing() );

        // activate feature naming
        if ( _options.featureName().isSet() )
            sub.setFeatureNameExpr( *_options.featureName() );

        osg::Node* node = sub.push( workingSet, localCX );
        if ( node )
        {
            resultGroup->addChild( node );

            // enable auto scaling on the group?
            if ( model && model->autoScale() == true )
            {
                resultGroup->getOrCreateStateSet()->setRenderBinDetails(0, osgEarth::AUTO_SCALE_BIN );
            }
        }
    }

    // extruded geometry
    if ( extrusion )
    {
        if ( altRequired )
        {
            AltitudeFilter clamp;
            clamp.setPropertiesFromStyle( style );
            sharedCX = clamp.push( workingSet, sharedCX );
            altRequired = false;
        }

        ExtrudeGeometryFilter extrude;
        extrude.setStyle( style );

        // Activate texture arrays if the GPU supports them and if the user
        // hasn't disabled them.        
        extrude.useTextureArrays() =
            Registry::capabilities().supportsTextureArrays() &&
            _options.useTextureArrays() == true;

        // apply per-feature naming if requested.
        if ( _options.featureName().isSet() )
            extrude.setFeatureNameExpr( *_options.featureName() );
        if ( _options.useVertexBufferObjects().isSet())
            extrude.useVertexBufferObjects() = *_options.useVertexBufferObjects();

        osg::Node* node = extrude.push( workingSet, sharedCX );
        if ( node )
        {
            resultGroup->addChild( node );
        }
        
    }

    // simple geometry
    else if ( point || line || polygon )
    {
        if ( altRequired )
        {
            AltitudeFilter clamp;
            clamp.setPropertiesFromStyle( style );
            sharedCX = clamp.push( workingSet, sharedCX );
            altRequired = false;
        }

        BuildGeometryFilter filter( style );
        if ( _options.maxGranularity().isSet() )
            filter.maxGranularity() = *_options.maxGranularity();
        if ( _options.geoInterp().isSet() )
            filter.geoInterp() = *_options.geoInterp();
        if ( _options.featureName().isSet() )
            filter.featureName() = *_options.featureName();

        osg::Node* node = filter.push( workingSet, sharedCX );
        if ( node )
        {
            resultGroup->addChild( node );
        }
    }

    if ( text || icon )
    {
        if ( altRequired )
        {
            AltitudeFilter clamp;
            clamp.setPropertiesFromStyle( style );
            sharedCX = clamp.push( workingSet, sharedCX );
            altRequired = false;
        }

        BuildTextFilter filter( style );
        osg::Node* node = filter.push( workingSet, sharedCX );
        if ( node )
        {
            resultGroup->addChild( node );
        }
    }

    if (Registry::capabilities().supportsGLSL())
    {
        if ( _options.shaderPolicy() == SHADERPOLICY_GENERATE )
        {
            // no ss cache because we will optimize later.
            Registry::shaderGenerator().run( 
                resultGroup.get(),
                "osgEarth.GeomCompiler" );
        }
        else if ( _options.shaderPolicy() == SHADERPOLICY_DISABLE )
        {
            resultGroup->getOrCreateStateSet()->setAttributeAndModes(
                new osg::Program(),
                osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE );
        }
    }

    // Optimize stateset sharing.
    if ( _options.optimizeStateSharing() == true )
    {
        // Common state set cache?
        osg::ref_ptr<StateSetCache> sscache;
        if ( sharedCX.getSession() )
        {
            // with a shared cache, don't combine statesets. They may be
            // in the live graph
            sscache = sharedCX.getSession()->getStateSetCache();
            sscache->consolidateStateAttributes( resultGroup.get() );
        }
        else 
        {
            // isolated: perform full optimization
            sscache = new StateSetCache();
            sscache->optimize( resultGroup.get() );
        }
    }

    //test: dump the tile to disk
    //osgDB::writeNodeFile( *(resultGroup.get()), "out.osg" );

#ifdef PROFILING
    osg::Timer_t p_end = osg::Timer::instance()->tick();
    OE_INFO << LC
        << "features = " << p_features
        << ", time = " << osg::Timer::instance()->delta_s(p_start, p_end) << " s." << std::endl;
#endif

    return resultGroup.release();
}
    //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;            
    }
Пример #9
0
osg::Node*
Graticule::createGridLevel( unsigned int levelNum ) const
{
    if ( !_map->isGeocentric() )
    {
        OE_WARN << "Graticule: only supports geocentric maps" << std::endl;
        return 0L;
    }

    Graticule::Level level;
    if ( !getLevel( levelNum, level ) )
        return 0L;

    OE_DEBUG << "Graticule: creating grid level " << levelNum << std::endl;

    osg::Group* group = new osg::Group();

    const Profile* mapProfile = _map->getProfile();
    const GeoExtent& pex = mapProfile->getExtent();

    double tw = pex.width() / (double)level._cellsX;
    double th = pex.height() / (double)level._cellsY;

    for( unsigned int x=0; x<level._cellsX; ++x )
    {
        for( unsigned int y=0; y<level._cellsY; ++y )
        {
            GeoExtent tex(
                mapProfile->getSRS(),
                pex.xMin() + tw * (double)x,
                pex.yMin() + th * (double)y,
                pex.xMin() + tw * (double)(x+1),
                pex.yMin() + th * (double)(y+1) );

            double ox = level._lineWidth;
            double oy = level._lineWidth;

            Geometry* geom = createCellGeometry( tex, level._lineWidth, pex, _map->isGeocentric() );

            Feature* feature = new Feature();
            feature->setGeometry( geom );
            FeatureList features;
            features.push_back( feature );

            FilterContext cx;
            cx.profile() = new FeatureProfile( tex );
            cx.isGeocentric() = _map->isGeocentric();

            if ( _map->isGeocentric() )
            {
                // We need to make sure that on a round globe, the points are sampled such that
                // long segments follow the curvature of the earth.
                ResampleFilter resample;
                resample.maxLength() = tex.width() / 10.0;
                resample.perturbationThreshold() = level._lineWidth/1000.0;
                cx = resample.push( features, cx );
            }

            TransformFilter xform( mapProfile->getSRS() );
            xform.setMakeGeocentric( _map->isGeocentric() );
            cx = xform.push( features, cx );

            Bounds bounds = feature->getGeometry()->getBounds();
            double exDist = bounds.radius()/2.0;

            osg::Node* cellVolume = createVolume(
                feature->getGeometry(),
                -exDist,
                exDist*2,
                cx );

            osg::Node* child = cellVolume;

            if ( cx.hasReferenceFrame() )
            {
                osg::MatrixTransform* xform = new osg::MatrixTransform( cx.inverseReferenceFrame() );
                xform->addChild( child );

                // the transform matrix here does NOT include a rotation, so we need to get the normal
                // for the cull plane callback.
                osg::Vec3d normal = xform->getBound().center();
                xform->setCullCallback( new CullPlaneCallback( normal ) );

                child = xform;
            }

            group->addChild( child );
        }
    }

    // organize it for better culling
    osgUtil::Optimizer opt;
    opt.optimize( group, osgUtil::Optimizer::SPATIALIZE_GROUPS );

    osg::Node* result = group;

    if ( levelNum+1 < getNumLevels() )
    {
        Graticule::Level nextLevel;
        if ( getLevel( levelNum+1, nextLevel ) )
        {
            osg::PagedLOD* plod = new osg::PagedLOD();
            plod->addChild( group, nextLevel._maxRange, level._maxRange );
            std::stringstream buf;
            buf << levelNum+1 << "_" << getID() << "." << GRID_MARKER << "." << GRATICLE_EXTENSION;
            std::string bufStr = buf.str();
            plod->setFileName( 1, bufStr );
            plod->setRange( 1, 0, nextLevel._maxRange );
            result = plod;
        }
    }

    return result;
}