Exemple #1
0
v8::Handle<v8::Value>
JSFilterContext::ToWorldCallback(const v8::Arguments& args)
{
    FilterContext* context = V8Util::UnwrapObject<FilterContext>(args.Holder());

    if (context && args.Length() == 1 && args[0]->IsObject())
    {
        v8::Local<v8::Object> obj( v8::Object::Cast(*args[0]) );

        /*if (V8Util::CheckObjectType(obj, JSGeometry::GetObjectType()))  // Geometry
        {
          osgEarth::Symbology::Geometry* geometry = V8Util::UnwrapObject<osgEarth::Symbology::Geometry>(obj);

          return
        }*/

        if (V8Util::CheckObjectType(obj, JSVec3d::GetObjectType()))  // Vec3d
        {
            osg::Vec3d* vec = V8Util::UnwrapObject<osg::Vec3d>(obj);
            osg::Vec3d* world = new osg::Vec3d(context->toWorld(*vec));
            return JSVec3d::WrapVec3d(world, true);
        }
    }

    return v8::Undefined();
}
    FeatureCursor* createFeatureCursor( const Symbology::Query& query )
    {
        FeatureCursor* result = 0L;

        std::string url = createURL( query );        

        // check the blacklist:
        if ( Registry::instance()->isBlacklisted(url) )
            return 0L;

        OE_DEBUG << LC << url << std::endl;
        URI uri(url);

        // read the data:
        ReadResult r = uri.readString( _dbOptions.get() );

        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
            const std::string& mimeType = r.metadata().value( IOMetadata::CONTENT_TYPE );
            dataOK = getFeatures( buffer, 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 (!_options.filters().empty())
        {
            // preprocess the features using the filter list:
            if ( features.size() > 0 )
            {
                FilterContext cx;
                cx.setProfile( getFeatureProfile() );

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

        //result = new FeatureListCursor(features);
        result = dataOK ? new FeatureListCursor( features ) : 0L;

        if ( !result )
            Registry::instance()->blacklist( url );

        return result;
    }
// reads a chunk of features into a memory cache; do this for performance
// and to avoid needing the OGR Mutex every time
void
FeatureCursorOGR::readChunk()
{
    if ( !_resultSetHandle )
        return;
    
    OGR_SCOPED_LOCK;

    while( _queue.size() < _chunkSize && !_resultSetEndReached )
    {
        FeatureList filterList;
        while( filterList.size() < _chunkSize && !_resultSetEndReached )
        {
            OGRFeatureH handle = OGR_L_GetNextFeature( _resultSetHandle );
            if ( handle )
            {
                osg::ref_ptr<Feature> feature = OgrUtils::createFeature( handle, _profile.get() );

                if (feature.valid() &&
                    !_source->isBlacklisted( feature->getFID() ) &&
                    validateGeometry( feature->getGeometry() ))
                {
                    filterList.push_back( feature.release() );
                }
                OGR_F_Destroy( handle );
            }
            else
            {
                _resultSetEndReached = true;
            }
        }

        // preprocess the features using the filter list:
        if ( !_filters.empty() )
        {
            FilterContext cx;
            cx.setProfile( _profile.get() );
            if (_query.bounds().isSet())
            {
                cx.extent() = GeoExtent(_profile->getSRS(), _query.bounds().get());
            }
            else
            {
                cx.extent() = _profile->getExtent();
            }

            for( FeatureFilterList::const_iterator i = _filters.begin(); i != _filters.end(); ++i )
            {
                FeatureFilter* filter = i->get();
                cx = filter->push( filterList, cx );
            }
        }

        for(FeatureList::const_iterator i = filterList.begin(); i != filterList.end(); ++i)
        {
            _queue.push( i->get() );
        }
    }
}
void FeatureCursorCDBV::Read_Data_Directly(void)
{
	FeatureList preProcessList;

	OGR_SCOPED_LOCK;

	OGR_L_ResetReading(_layerHandle);
	OGRFeatureH feat_handle;

	OGRLayer * thislayer = (OGRLayer *)_layerHandle;
	int totalCount = thislayer->GetFeatureCount();
	int fcount = -1;
	while ((feat_handle = OGR_L_GetNextFeature(_layerHandle)) != NULL)
	{
		++fcount;
		if (feat_handle)
		{
			osg::ref_ptr<Feature> f = OgrUtils::createFeature(feat_handle, _profile.get());
			if (f.valid() && !_source->isBlacklisted(f->getFID()))
			{
				if (isGeometryValid(f->getGeometry()))
				{
					_queue.push(f);

					if (_filters.size() > 0)
					{
						preProcessList.push_back(f.release());
					}
				}
				else
				{
					OE_DEBUG << LC << "Skipping feature with invalid geometry: " << f->getGeoJSON() << std::endl;
				}
			}
			OGR_F_Destroy(feat_handle);
		}
	}

	// preprocess the features using the filter list:
	if (preProcessList.size() > 0)
	{
		FilterContext cx;
		cx.setProfile(_profile.get());

		for (FeatureFilterList::const_iterator i = _filters.begin(); i != _filters.end(); ++i)
		{
			FeatureFilter* filter = i->get();
			cx = filter->push(preProcessList, cx);
		}
	}

}
Exemple #5
0
FilterContext
ClampFilter::push( FeatureList& features, const FilterContext& cx )
{
    const Session* session = cx.getSession();
    if ( !session ) {
        OE_WARN << LC << "No session - session is required for elevation clamping" << std::endl;
        return cx;
    }

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

    const SpatialReference* mapSRS     = mapf.getProfile()->getSRS();
    const SpatialReference* featureSRS = cx.profile()->getSRS();
    bool isGeocentric = session->getMapInfo().isGeocentric();

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

    for( FeatureList::iterator i = features.begin(); i != features.end(); ++i )
    {
        Feature* feature = i->get();
        
        GeometryIterator gi( feature->getGeometry() );
        while( gi.hasMore() )
        {
            Geometry* geom = gi.next();

            if ( isGeocentric )
            {
                // convert to map coords:
                cx.toWorld( geom );
                mapSRS->transformFromECEF( geom );

                // populate the elevations:
                eq.getElevations( geom, mapSRS );

                // convert back to geocentric:
                mapSRS->transformToECEF( geom );
                cx.toLocal( geom );
            }

            else
            {
                // clamps the entire array to the highest available resolution.
                eq.getElevations( geom, featureSRS );
            }
        }
    }

    return cx;
}
bool
BuildGeometryFilter::pushTextAnnotation( TextAnnotation* anno, const FilterContext& context )
{
    // find the centroid
    osg::Vec3d centroid = anno->getGeometry()->getBounds().center();

    osgText::Text* t = new osgText::Text();
    t->setText( anno->text() );
    t->setFont( "fonts/arial.ttf" );
    t->setAutoRotateToScreen( true );
    t->setCharacterSizeMode( osgText::TextBase::SCREEN_COORDS );
    t->setCharacterSize( 32.0f );
    //t->setCharacterSizeMode( osgText::TextBase::OBJECT_COORDS_WITH_MAXIMUM_SCREEN_SIZE_CAPPED_BY_FONT_HEIGHT );
    //t->setCharacterSize( 300000.0f );
    t->setPosition( centroid );
    t->setAlignment( osgText::TextBase::CENTER_CENTER );
    t->getOrCreateStateSet()->setAttributeAndModes( new osg::Depth(osg::Depth::ALWAYS), osg::StateAttribute::ON );
    t->getOrCreateStateSet()->setRenderBinDetails( 99999, "RenderBin" );

    // apply styling as appropriate:
    osg::Vec4f textColor(1,1,1,1);
    osg::Vec4f haloColor(0,0,0,1);

    const TextSymbol* textSymbolizer = getStyle().getSymbol<TextSymbol>();
    if ( textSymbolizer )
    {
        textColor = textSymbolizer->fill()->color();
        if ( textSymbolizer->halo().isSet() )
        {
            haloColor = textSymbolizer->halo()->color();
        }
    }

    t->setColor( textColor );
    t->setBackdropColor( haloColor );
    t->setBackdropType( osgText::Text::OUTLINE );

    if ( context.isGeocentric() )
    {
        // install a cluster culler: note that the CCC control point and normal must be
        // in world coordinates
        const osg::EllipsoidModel* ellip = context.profile()->getSRS()->getEllipsoid();
        osg::Vec3d cp = centroid * context.inverseReferenceFrame();
        osg::Vec3d normal = ellip->computeLocalUpVector( cp.x(), cp.y(), cp.z() );
        osg::ClusterCullingCallback* ccc = new osg::ClusterCullingCallback( cp, normal, 0.0f );
        t->setCullCallback( ccc );
    }

    _geode->addDrawable( t );

    return true;    
}
void
TessellateOperator::operator()( Feature* feature, FilterContext& context ) const
{
    if (_numPartitions <= 1 ||
        !feature || 
        !feature->getGeometry() || 
        feature->getGeometry()->getComponentType() == Geometry::TYPE_POINTSET )
    {
        return;
    }

    bool isGeo = context.profile() ? context.profile()->getSRS()->isGeographic() : true;
    GeoInterpolation interp = feature->geoInterp().isSet() ? *feature->geoInterp() : _defaultInterp;

    GeometryIterator i( feature->getGeometry(), true );
    while( i.hasMore() )
    {
        Geometry* g = i.next();
        bool isRing = dynamic_cast<Ring*>( g ) != 0L;

        Vec3dVector newVerts;
        newVerts.reserve( g->size() * _numPartitions );

        for( Geometry::const_iterator v = g->begin(); v != g->end(); ++v )
        {
            const osg::Vec3d& p0 = *v;
            if ( v != g->end()-1 ) // not last vert
            {
                if ( isGeo )
                    tessellateGeo( *v, *(v+1), _numPartitions, interp, newVerts );
                else
                    tessellateLinear( *v, *(v+1), _numPartitions, newVerts );
            }
            else if ( isRing )
            {
                if ( isGeo )
                    tessellateGeo( *v, *g->begin(), _numPartitions, interp, newVerts );
                else
                    tessellateLinear( *v, *g->begin(), _numPartitions, newVerts );
            }
            else 
            {
                // get the final vert.
                newVerts.push_back( *v );
            }
        }

        g->swap( newVerts );
    }
}
FilterContext
TransformFilter::push( FeatureList& input, FilterContext& incx )
{
    _bbox = osg::BoundingBoxd();

    // first transform all the points into the output SRS, collecting a bounding box as we go:
    bool ok = true;
    for( FeatureList::iterator i = input.begin(); i != input.end(); i++ )
        if ( !push( i->get(), incx ) )
            ok = false;

    FilterContext outcx( incx );
    outcx.isGeocentric() = _makeGeocentric;

    if ( _outputSRS.valid() )
    {
        if ( incx.extent()->isValid() )
            outcx.profile() = new FeatureProfile( incx.extent()->transform( _outputSRS.get() ) );
        else
            outcx.profile() = new FeatureProfile( incx.profile()->getExtent().transform( _outputSRS.get() ) );
    }

    // set the reference frame to shift data to the centroid. This will
    // prevent floating point precision errors in the openGL pipeline for
    // properly gridded data.
    if ( _bbox.valid() && _localize )
    {
        // create a suitable reference frame:
        osg::Matrixd localizer;
        if ( _makeGeocentric )
        {
            localizer = createGeocentricInvRefFrame( _bbox.center(), _outputSRS.get() );
            localizer.invert( localizer );
        }
        else
        {
            localizer = osg::Matrixd::translate( -_bbox.center() );
        }

        // localize the geometry relative to the reference frame.
        for( FeatureList::iterator i = input.begin(); i != input.end(); i++ )
        {
            localizeGeometry( i->get(), localizer );
        }
        outcx.setReferenceFrame( localizer );
    }

    return outcx;
}
Exemple #9
0
void
FeatureSource::applyFilters(FeatureList& features, const GeoExtent& extent) const
{
    // apply filters before returning.
    if ( !getFilters().empty() )
    {
        FilterContext cx;
        cx.setProfile( getFeatureProfile() );
        cx.extent() = extent;
        for(FeatureFilterList::const_iterator filter = getFilters().begin(); filter != getFilters().end(); ++filter)
        {
            cx = filter->get()->push( features, cx );
        }
    }
}
void
FeatureSource::applyFilters(FeatureList& features, const GeoExtent& extent) const
{
    // apply filters before returning.
    if (_filters.valid() && _filters->empty() == false)
    {
        FilterContext cx;
        cx.setProfile( getFeatureProfile() );
        cx.extent() = extent;
        for(FeatureFilterChain::const_iterator filter = _filters->begin(); filter != _filters->end(); ++filter)
        {
            cx = filter->get()->push( features, cx );
        }
    }
}
Exemple #11
0
BufferSrcFilterContext::BufferSrcFilterContext(const FilterContext &baseContext)
{
    if (baseContext && isFilterValid(baseContext.getFilter()))
    {
        makeRef(baseContext);
    }
}
FilterContext
AltitudeFilter::push( FeatureList& features, FilterContext& cx )
{
    bool clamp = 
        _altitude.valid() && 
        _altitude->clamping() != AltitudeSymbol::CLAMP_NONE &&
        cx.getSession()       != 0L &&
        cx.profile()          != 0L;

    if ( clamp )
        pushAndClamp( features, cx );
    else
        pushAndDontClamp( features, cx );

    return cx;
}
Exemple #13
0
    osg::Vec3dArray* createBoundary( const SpatialReference* srs, ProgressCallback* progress )
    {
        if ( _failed )
            return 0L;

        if ( _features.valid() )
        {
            if ( _features->getFeatureProfile() )
            {
                osg::ref_ptr<FeatureCursor> cursor = _features->createFeatureCursor();
                if ( cursor )
                {
                    if ( cursor->hasMore() )
                    {
                        Feature* f = cursor->nextFeature();
                        if ( f && f->getGeometry() )
                        {
                            // Init a filter to tranform feature in desired SRS 
                            if (!srs->isEquivalentTo(_features->getFeatureProfile()->getSRS())) {
                                FilterContext cx;
                                cx.profile() = new FeatureProfile(_features->getFeatureProfile()->getExtent());
                                //cx.isGeocentric() = _features->getFeatureProfile()->getSRS()->isGeographic();

                                TransformFilter xform( srs );
                                FeatureList featureList;
                                featureList.push_back(f);
                                cx = xform.push(featureList, cx);
                            }

                            return f->getGeometry()->toVec3dArray();
                        }
                    }
                }
            }
            else
            {
                OE_WARN << LC << "Failed to create boundary; feature source has no SRS" << std::endl;
                _failed = true;
            }
        }
        else
        {
            OE_WARN << LC << "Unable to create boundary; invalid feature source" << std::endl;
            _failed = true;
        }
        return 0L;
    }
Exemple #14
0
bool
TransformFilter::push( Feature* input, FilterContext& context )
{
    if ( !input || !input->getGeometry() )
        return true;

    bool needsSRSXform =
        _outputSRS.valid() &&
        ( ! context.profile()->getSRS()->isEquivalentTo( _outputSRS.get() ) );

    bool needsMatrixXform = !_mat.isIdentity();

    // optimize: do nothing if nothing needs doing
    if ( !needsSRSXform && !_makeGeocentric && !_localize && !needsMatrixXform )
        return true;

    // iterate over the feature geometry.
    GeometryIterator iter( input->getGeometry() );
    while( iter.hasMore() )
    {
        Geometry* geom = iter.next();

        // pre-transform the point before doing an SRS transformation.
        if ( needsMatrixXform )
        {
            for( unsigned i=0; i < geom->size(); ++i )
                (*geom)[i] = (*geom)[i] * _mat;
        }

        // first transform the geometry to the output SRS:            
        if ( needsSRSXform )
            context.profile()->getSRS()->transformPoints( _outputSRS.get(), geom->asVector(), false );

        // convert to ECEF if required:
        if ( _makeGeocentric )
            _outputSRS->transformToECEF( geom->asVector(), false );

        // update the bounding box.
        if ( _localize )
        {
            for( unsigned i=0; i<geom->size(); ++i )
                _bbox.expandBy( (*geom)[i] );
        }
    }

    return true;
}
Exemple #15
0
Feature*
GeometryFeatureCursor::nextFeature()
{
    if ( hasMore() )
    {        
        _lastFeature = new Feature( _geom.get(), _featureProfile.valid() ? _featureProfile->getSRS() : 0L );
        FilterContext cx;
        cx.profile() = _featureProfile.get();
        FeatureList list;
        list.push_back( _lastFeature.get() );
        for( FeatureFilterList::const_iterator i = _filters.begin(); i != _filters.end(); ++i ) {
            cx = i->get()->push( list, cx );
        }
        _geom = 0L;
    }
    return _lastFeature.get();
}
Exemple #16
0
void BufferSinkFilterContext::assign(const FilterContext &ctx, OptionalErrorCode ec)
{
    clear_if(ec);
    m_type = checkFilter(ctx.filter());
    if (m_type == FilterMediaType::Unknown) {
        throws_if(ec, Errors::IncorrectBufferSinkFilter);
        return;
    }
    m_sink = ctx;
}
Exemple #17
0
v8::Handle<v8::Value>
JSFilterContext::FromMapCallback(const v8::Arguments& args)
{
    FilterContext* context = V8Util::UnwrapObject<FilterContext>(args.Holder());

    if (context && args.Length() == 1 && args[0]->IsObject())
    {
        v8::Local<v8::Object> obj( v8::Object::Cast(*args[0]) );

        if (V8Util::CheckObjectType(obj, JSVec3d::GetObjectType()))  // Vec3d
        {
            osg::Vec3d* map = V8Util::UnwrapObject<osg::Vec3d>(obj);
            osg::Vec3d* local = new osg::Vec3d(context->fromMap(*map));
            return JSVec3d::WrapVec3d(local, true);
        }
    }

    return v8::Undefined();
}
Exemple #18
0
v8::Handle<v8::Value>
JSFilterContext::PropertyCallback(v8::Local<v8::String> name, const v8::AccessorInfo& info)
{
    FilterContext* context = V8Util::UnwrapObject<FilterContext>(info.Holder());

    v8::String::Utf8Value utf8_value(name);
    std::string prop(*utf8_value);

    if (!context || prop.empty())
        return v8::Handle<v8::Value>();

    if (prop == "session")
        return JSSession::WrapSession(const_cast<Session*>(context->getSession()));
    if (prop == "profile")
        return JSFeatureProfile::WrapFeatureProfile(const_cast<FeatureProfile*>(context->profile().get()));
    if (prop == "extent" && context->extent().isSet())
        return JSGeoExtent::WrapGeoExtent(const_cast<osgEarth::GeoExtent*>(&context->extent().get()));
    //if (prop == "geocentric")
    //  return v8::Boolean::New(context->isGeocentric());

    return v8::Handle<v8::Value>();
}
Exemple #19
0
void FilterContext::link(unsigned srcPad, FilterContext &dstFilter, unsigned dstPad, error_code &ec)
{
    clear_if(ec);
    if (!m_raw || !dstFilter) {
        throws_if(ec, Errors::Unallocated);
        return;
    }

    int sts = avfilter_link(m_raw, srcPad, dstFilter.raw(), dstPad);
    if (sts < 0) {
        throws_if(ec, sts, ffmpeg_category());
        return;
    }
}
Exemple #20
0
void
FeaturesToNodeFilter::computeLocalizers( const FilterContext& context, const osgEarth::GeoExtent &extent, osg::Matrixd &out_w2l, osg::Matrixd &out_l2w )
{
    if ( context.isGeoreferenced() )
    {
        if ( context.getSession()->getMapInfo().isGeocentric() )
        {
            const SpatialReference* geogSRS = context.profile()->getSRS()->getGeographicSRS();
            GeoExtent geodExtent = extent.transform( geogSRS );
            if ( geodExtent.width() < 180.0 )
            {
                osg::Vec3d centroid, centroidECEF;
                geodExtent.getCentroid( centroid.x(), centroid.y() );
                geogSRS->transform( centroid, geogSRS->getECEF(), centroidECEF );
                geogSRS->getECEF()->createLocalToWorld( centroidECEF, out_l2w );
                out_w2l.invert( out_l2w );
            }
        }

        else // projected
        {
            if ( extent.isValid() )
            {
                osg::Vec3d centroid;
                extent.getCentroid(centroid.x(), centroid.y());

                extent.getSRS()->transform(
                    centroid,
                    context.getSession()->getMapInfo().getProfile()->getSRS(),
                    centroid );

                out_w2l.makeTranslate( -centroid );
                out_l2w.invert( out_w2l );
            }
        }
    }
}
Exemple #21
0
FilterContext
ScatterFilter::push(FeatureList& features, 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
    _prng = Random( _randomSeed, Random::METHOD_FAST );

    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();

        osg::ref_ptr< PointSet > points = new PointSet();

        if ( geom->getComponentType() == Geometry::TYPE_POLYGON )
        {
            polyScatter( geom, geomSRS, context, points.get() );
        }
        else if (
            geom->getComponentType() == Geometry::TYPE_LINESTRING ||
            geom->getComponentType() == Geometry::TYPE_RING )            
        {
            lineScatter( geom, geomSRS, context, points.get() );
        }
        else {            
            points = static_cast< PointSet*>(geom->cloneAs(Geometry::TYPE_POINTSET));
        }

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

    return context;
}
bool
SubstituteModelFilter::findResource(const URI&            uri,
                                    const InstanceSymbol* symbol,
                                    FilterContext&        context, 
                                    std::set<URI>&        missing,
                                    osg::ref_ptr<InstanceResource>& output )
{
    // be careful about refptrs here since _instanceCache is an LRU.

    InstanceCache::Record rec;
    if ( _instanceCache.get(uri, rec) )
    {
        // found it in the cache:
        output = rec.value().get();
    }
    else if ( _resourceLib.valid() )
    {
        // look it up in the resource library:
        output = _resourceLib->getInstance( uri.base(), context.getDBOptions() );
    }
    else
    {
        // create it on the fly:
        output = symbol->createResource();
        output->uri() = uri;
        _instanceCache.insert( uri, output.get() );
    }

    // failed to find the instance.
    if ( !output.valid() )
    {
        if ( missing.find(uri) == missing.end() )
        {
            missing.insert(uri);
            OE_WARN << LC << "Failed to locate resource: " << uri.full() << std::endl;
        }
    }

    return output.valid();
}
Exemple #23
0
void
FeaturesToNodeFilter::computeLocalizers( const FilterContext& context )
{
    if ( context.isGeoreferenced() )
    {
        if ( context.getSession()->getMapInfo().isGeocentric() )
        {
            const SpatialReference* geogSRS = context.profile()->getSRS()->getGeographicSRS();
            GeoExtent geodExtent = context.extent()->transform( geogSRS );
            if ( geodExtent.width() < 180.0 )
            {
                osg::Vec3d centroid, centroidECEF;
                geodExtent.getCentroid( centroid.x(), centroid.y() );
                geogSRS->transform( centroid, geogSRS->getECEF(), centroidECEF );
                geogSRS->getECEF()->createLocalToWorld( centroidECEF, _local2world );
                _world2local.invert( _local2world );
            }
        }

        else // projected
        {
            if ( context.extent().isSet() )
            {
                osg::Vec3d centroid;
                context.extent()->getCentroid(centroid.x(), centroid.y());

                context.extent()->getSRS()->transform(
                    centroid,
                    context.getSession()->getMapInfo().getProfile()->getSRS(),
                    centroid );

                _world2local.makeTranslate( -centroid );
                _local2world.invert( _world2local );
            }
        }
    }
}
void
ExtrudeGeometryFilter::reset( const FilterContext& context )
{
    _cosWallAngleThresh = cos( _wallAngleThresh_deg );
    _geodes.clear();
    
    if ( _styleDirty )
    {
        const StyleSheet* sheet = context.getSession() ? context.getSession()->styles() : 0L;

        _wallSkinSymbol    = 0L;
        _wallPolygonSymbol = 0L;
        _roofSkinSymbol    = 0L;
        _roofPolygonSymbol = 0L;
        _extrusionSymbol   = 0L;
        _outlineSymbol     = 0L;

        _extrusionSymbol = _style.get<ExtrusionSymbol>();
        if ( _extrusionSymbol.valid() )
        {
            // make a copy of the height expression so we can use it:
            if ( _extrusionSymbol->heightExpression().isSet() )
            {
                _heightExpr = *_extrusionSymbol->heightExpression();
            }

            // If there is no height expression, and we have either absolute or terrain-relative
            // clamping, THAT means that we want to extrude DOWN from the geometry to the ground
            // (instead of from the geometry.)
            AltitudeSymbol* alt = _style.get<AltitudeSymbol>();
            if ( alt && !_extrusionSymbol->heightExpression().isSet() && !_extrusionSymbol->height().isSet() )
            {
                if (alt->clamping() == AltitudeSymbol::CLAMP_ABSOLUTE ||
                    alt->clamping() == AltitudeSymbol::CLAMP_RELATIVE_TO_TERRAIN )
                {
                    _heightExpr = NumericExpression( "0-[__max_hat]" );
                }
            }
            
            // attempt to extract the wall symbols:
            if ( _extrusionSymbol->wallStyleName().isSet() && sheet != 0L )
            {
                const Style* wallStyle = sheet->getStyle( *_extrusionSymbol->wallStyleName(), false );
                if ( wallStyle )
                {
                    _wallSkinSymbol = wallStyle->get<SkinSymbol>();
                    _wallPolygonSymbol = wallStyle->get<PolygonSymbol>();
                }
            }

            // attempt to extract the rooftop symbols:
            if ( _extrusionSymbol->roofStyleName().isSet() && sheet != 0L )
            {
                const Style* roofStyle = sheet->getStyle( *_extrusionSymbol->roofStyleName(), false );
                if ( roofStyle )
                {
                    _roofSkinSymbol = roofStyle->get<SkinSymbol>();
                    _roofPolygonSymbol = roofStyle->get<PolygonSymbol>();
                }
            }

            // if there's a line symbol, use it to outline the extruded data.
            _outlineSymbol = _style.get<LineSymbol>();
        }

        // backup plan for skin symbols:
        const SkinSymbol* skin = _style.get<SkinSymbol>();
        if ( skin )
        {
            if ( !_wallSkinSymbol.valid() )
                _wallSkinSymbol = skin;
            if ( !_roofSkinSymbol.valid() )
                _roofSkinSymbol = skin;
        }

        // backup plan for poly symbols:
        const PolygonSymbol* poly = _style.get<PolygonSymbol>();
        if ( poly )
        {
            if ( !_wallPolygonSymbol.valid() )
                _wallPolygonSymbol = poly;
            if ( !_roofPolygonSymbol.valid() )
                _roofPolygonSymbol = poly;
        }

        _styleDirty = false;
    }
}
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;
}
bool
ExtrudeGeometryFilter::buildStructure(const Geometry*         input,
                                      double                  height,
                                      double                  heightOffset,
                                      bool                    flatten,
                                      const SkinResource*     wallSkin,
                                      const SkinResource*     roofSkin,
                                      Structure&              structure,
                                      FilterContext&          cx )
{
    bool  makeECEF                 = false;
    const SpatialReference* srs    = 0L;
    const SpatialReference* mapSRS = 0L;

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

    // whether this is a closed polygon structure.
    structure.isPolygon = (input->getComponentType() == Geometry::TYPE_POLYGON);

    // extrusion working variables
    double     targetLen = -DBL_MAX;
    osg::Vec3d minLoc(DBL_MAX, DBL_MAX, DBL_MAX);
    double     minLoc_len = DBL_MAX;
    osg::Vec3d maxLoc(0,0,0);
    double     maxLoc_len = 0;

    // Initial pass over the geometry does two things:
    // 1: Calculate the minimum Z across all parts.
    // 2: Establish a "target length" for extrusion
    double absHeight = fabs(height);

    ConstGeometryIterator zfinder( input );
    while( zfinder.hasMore() )
    {
        const Geometry* geom = zfinder.next();
        for( Geometry::const_iterator m = geom->begin(); m != geom->end(); ++m )
        {
            osg::Vec3d m_point = *m;

            if ( m_point.z() + absHeight > targetLen )
                targetLen = m_point.z() + absHeight;

            if (m_point.z() < minLoc.z())
                minLoc = m_point;

            if (m_point.z() > maxLoc.z())
                maxLoc = m_point;
        }
    }

    // apply the height offsets
    height    -= heightOffset;
    targetLen -= heightOffset;
    
    float   roofRotation  = 0.0f;
    Bounds  roofBounds;
    float   sinR = 0.0f, cosR = 0.0f;
    double  roofTexSpanX = 0.0, roofTexSpanY = 0.0;
    osg::ref_ptr<const SpatialReference> roofProjSRS;

    if ( roofSkin )
    {
        roofBounds = input->getBounds();

        // if our data is lat/long, we need to reproject the geometry and the bounds into a projected
        // coordinate system in order to properly generate tex coords.
        if ( srs && srs->isGeographic() )
        {
            osg::Vec2d geogCenter = roofBounds.center2d();
            roofProjSRS = srs->createUTMFromLonLat( Angle(geogCenter.x()), Angle(geogCenter.y()) );
            if ( roofProjSRS.valid() )
            {
                roofBounds.transform( srs, roofProjSRS.get() );
                osg::ref_ptr<Geometry> projectedInput = input->clone();
                srs->transform( projectedInput->asVector(), roofProjSRS.get() );
                roofRotation = getApparentRotation( projectedInput.get() );
            }
        }
        else
        {
            roofRotation = getApparentRotation( input );
        }
            
        sinR = sin(roofRotation);
        cosR = cos(roofRotation);

        if ( !roofSkin->isTiled().value() )
        {
            //note: non-tiled roofs don't really work atm.
            roofTexSpanX = cosR*roofBounds.width() - sinR*roofBounds.height();
            roofTexSpanY = sinR*roofBounds.width() + cosR*roofBounds.height();
        }
        else
        {
            roofTexSpanX = roofSkin->imageWidth().isSet() ? *roofSkin->imageWidth() : roofSkin->imageHeight().isSet() ? *roofSkin->imageHeight() : 10.0;
            if ( roofTexSpanX <= 0.0 ) roofTexSpanX = 10.0;
            roofTexSpanY = roofSkin->imageHeight().isSet() ? *roofSkin->imageHeight() : roofSkin->imageWidth().isSet() ? *roofSkin->imageWidth() : 10.0;
            if ( roofTexSpanY <= 0.0 ) roofTexSpanY = 10.0;
        }
    }

    // prep for wall texture coordinate generation.
    double texWidthM  = wallSkin ? *wallSkin->imageWidth() : 0.0;
    double texHeightM = wallSkin ? *wallSkin->imageHeight() : 1.0;

    ConstGeometryIterator iter( input );
    while( iter.hasMore() )
    {
        const Geometry* part = iter.next();

        // skip a part that's too small
        if (part->size() < 2)
            continue;

        // add a new wall.
        structure.elevations.push_back(Elevation());
        Elevation& elevation = structure.elevations.back();

        double maxHeight = targetLen - minLoc.z();

        // Adjust the texture height so it is a multiple of the maximum height
        double div = osg::round(maxHeight / texHeightM);
        elevation.texHeightAdjustedM = div > 0.0 ? maxHeight / div : maxHeight;

        // Step 1 - Create the real corners and transform them into our target SRS.
        Corners corners;
        for(Geometry::const_iterator m = part->begin(); m != part->end(); ++m)
        {
            Corners::iterator corner = corners.insert(corners.end(), Corner());
            
            // mark as "from source", as opposed to being inserted by the algorithm.
            corner->isFromSource = true;
            corner->base = *m;

            // extrude:
            if ( height >= 0 ) // extrude up
            {
                if ( flatten )
                    corner->roof.set( corner->base.x(), corner->base.y(), targetLen );
                else
                    corner->roof.set( corner->base.x(), corner->base.y(), corner->base.z() + height );
            }
            else // height < 0 .. extrude down
            {
                corner->roof = *m;
                corner->base.z() += height;
            }
            
            // figure out the rooftop texture coords before doing any transformation:
            if ( roofSkin && srs )
            {
                double xr, yr;

                if ( srs && srs->isGeographic() && roofProjSRS )
                {
                    osg::Vec3d projRoofPt;
                    srs->transform( corner->roof, roofProjSRS.get(), projRoofPt );
                    xr = (projRoofPt.x() - roofBounds.xMin());
                    yr = (projRoofPt.y() - roofBounds.yMin());
                }
                else
                {
                    xr = (corner->roof.x() - roofBounds.xMin());
                    yr = (corner->roof.y() - roofBounds.yMin());
                }

                corner->roofTexU = (cosR*xr - sinR*yr) / roofTexSpanX;
                corner->roofTexV = (sinR*xr + cosR*yr) / roofTexSpanY;
            }
            
            // transform into target SRS.
            transformAndLocalize( corner->base, srs, corner->base, mapSRS, _world2local, makeECEF );
            transformAndLocalize( corner->roof, srs, corner->roof, mapSRS, _world2local, makeECEF );
        }

        // Step 2 - Insert intermediate Corners as needed to satify texturing
        // requirements (if necessary) and record each corner offset (horizontal distance
        // from the beginning of the part geometry to the corner.)
        double cornerOffset    = 0.0;
        double nextTexBoundary = texWidthM;

        for(Corners::iterator c = corners.begin(); c != corners.end(); ++c)
        {
            Corners::iterator this_corner = c;

            Corners::iterator next_corner = c;
            if ( ++next_corner == corners.end() )
                next_corner = corners.begin();

            osg::Vec3d base_vec = next_corner->base - this_corner->base;
            double span = base_vec.length();

            this_corner->offsetX = cornerOffset;

            if (wallSkin)
            {
                base_vec /= span; // normalize
                osg::Vec3d roof_vec = next_corner->roof - this_corner->roof;
                roof_vec.normalize();

                while(nextTexBoundary < cornerOffset+span)
                {
                    // insert a new fake corner.
                    Corners::iterator new_corner = corners.insert(next_corner, Corner());
                    new_corner->isFromSource = false;
                    double advance = nextTexBoundary-cornerOffset;
                    new_corner->base = this_corner->base + base_vec*advance;
                    new_corner->roof = this_corner->roof + roof_vec*advance;
                    new_corner->offsetX = cornerOffset + advance;
                    nextTexBoundary += texWidthM;

                    // advance the main iterator
                    c = new_corner;
                }
            }

            cornerOffset += span;
        }

        // Step 3 - Calculate the angle of each corner.
        osg::Vec3d prev_vec;
        for(Corners::iterator c = corners.begin(); c != corners.end(); ++c)
        {
            Corners::const_iterator this_corner = c;

            Corners::const_iterator next_corner = c;
            if ( ++next_corner == corners.end() )
                next_corner = corners.begin();

            if ( this_corner == corners.begin() )
            {
                Corners::const_iterator prev_corner = corners.end();
                --prev_corner;
                prev_vec = this_corner->roof - prev_corner->roof;
                prev_vec.normalize();
            }

            osg::Vec3d this_vec = next_corner->roof - this_corner->roof;
            this_vec.normalize();
            if ( c != corners.begin() )
            {
                c->cosAngle = prev_vec * this_vec;
            }
        }

        // Step 4 - Create faces connecting each pair of Posts.
        Faces& faces = elevation.faces;
        for(Corners::const_iterator c = corners.begin(); c != corners.end(); ++c)
        {
            Corners::const_iterator this_corner = c;

            Corners::const_iterator next_corner = c;
            if ( ++next_corner == corners.end() )
                next_corner = corners.begin();
            
            // only close the shape for polygons.
            if (next_corner != corners.begin() || structure.isPolygon)
            {
                faces.push_back(Face());
                Face& face = faces.back();
                face.left  = *this_corner;
                face.right = *next_corner;

                // recalculate the final offset on the last face
                if ( next_corner == corners.begin() )
                {
                    osg::Vec3d vec = next_corner->roof - this_corner->roof;
                    face.right.offsetX = face.left.offsetX + vec.length();
                }

                face.widthM = next_corner->offsetX - this_corner->offsetX;
            }
        }
    }

    return true;
}
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;
}
Exemple #28
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 );
        }
    }
}
Exemple #29
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;
}
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;
}