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;    
}
Exemple #2
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.getNode();

            if ( cx.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;
}
Exemple #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) );

            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;
}
bool
BuildGeometryFilter::pushRegularFeature( Feature* input, const FilterContext& context )
{
    GeometryIterator parts( input->getGeometry(), false );
    while( parts.hasMore() )
    {
        Geometry* part = parts.next();
        
        osg::PrimitiveSet::Mode primMode = osg::PrimitiveSet::POINTS;

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

        osg::Vec4f color = osg::Vec4(1,1,1,1);
        bool tessellatePolys = true;

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

        switch( part->getType() )
        {
        case Geometry::TYPE_POINTSET:
            {
                _hasPoints = true;
                primMode = osg::PrimitiveSet::POINTS;
                const PointSymbol* point = myStyle.getSymbol<PointSymbol>();
                if (point)
                {
                    color = point->fill()->color();
                }
            }
            break;

        case Geometry::TYPE_LINESTRING:
            {
                _hasLines = true;
                primMode = osg::PrimitiveSet::LINE_STRIP;
                const LineSymbol* lineSymbol = myStyle.getSymbol<LineSymbol>();
                if (lineSymbol)
                {
                    color = lineSymbol->stroke()->color();
                    width = lineSymbol->stroke()->width().isSet() ? *lineSymbol->stroke()->width() : 1.0f;
                }
            }
            break;

        case Geometry::TYPE_RING:
            {
                _hasLines = true;
                primMode = osg::PrimitiveSet::LINE_LOOP;
                const LineSymbol* lineSymbol = myStyle.getSymbol<LineSymbol>();
                if (lineSymbol)
                {
                    color = lineSymbol->stroke()->color();
                    width = lineSymbol->stroke()->width().isSet() ? *lineSymbol->stroke()->width() : 1.0f;
                }
            }
            break;

        case Geometry::TYPE_POLYGON:
            {
                primMode = osg::PrimitiveSet::LINE_LOOP; // loop will tessellate into polys
                const PolygonSymbol* poly = myStyle.getSymbol<PolygonSymbol>();
                if (poly)
                {
                    _hasPolygons = true;
                    color = poly->fill()->color();
                }
                else
                {
                    // if we have a line symbol and no polygon symbol, draw as an outline.
                    _hasLines = true;
                    const LineSymbol* line = myStyle.getSymbol<LineSymbol>();
                    if ( line )
                    {
                        color = line->stroke()->color();
                        width = line->stroke()->width().isSet() ? *line->stroke()->width() : 1.0f;
                        tessellatePolys = false;
                    }
                }
            }
            break;
        }
        
        osg::Geometry* osgGeom = new osg::Geometry();

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

        osgGeom->setUseVertexBufferObjects( true );
        osgGeom->setUseDisplayList( false );

        if ( setWidth && width != 1.0f )
        {
            osgGeom->getOrCreateStateSet()->setAttributeAndModes(
                new osg::LineWidth( width ), osg::StateAttribute::ON );
        }
        
        if (part->getType() == Geometry::TYPE_POLYGON && static_cast<Polygon*>(part)->getHoles().size() > 0 )
        {
            Polygon* poly = static_cast<Polygon*>(part);
            int totalPoints = poly->getTotalPointCount();
            osg::Vec3Array* allPoints = new osg::Vec3Array( totalPoints );

            std::copy( part->begin(), part->end(), allPoints->begin() );
            osgGeom->addPrimitiveSet( new osg::DrawArrays( primMode, 0, part->size() ) );

            int offset = part->size();

            for( RingCollection::const_iterator h = poly->getHoles().begin(); h != poly->getHoles().end(); ++h )
            {
                Geometry* hole = h->get();
                if ( hole->isValid() )
                {
                    std::copy( hole->begin(), hole->end(), allPoints->begin() + offset );
                    osgGeom->addPrimitiveSet( new osg::DrawArrays( primMode, offset, hole->size() ) );
                    offset += hole->size();
                }
            }
            osgGeom->setVertexArray( allPoints );
        }
        else
        {
            osgGeom->setVertexArray( part->toVec3Array() );
            osgGeom->addPrimitiveSet( new osg::DrawArrays( primMode, 0, part->size() ) );
        }

        // tessellate all polygon geometries. Tessellating each geometry separately
        // with TESS_TYPE_GEOMETRY is much faster than doing the whole bunch together
        // using TESS_TYPE_DRAWABLE.

        if ( part->getType() == Geometry::TYPE_POLYGON && tessellatePolys )
        {
            osgUtil::Tessellator tess;
            //tess.setTessellationType( osgUtil::Tessellator::TESS_TYPE_DRAWABLE );
            //tess.setWindingType( osgUtil::Tessellator::TESS_WINDING_ODD );
            tess.setTessellationType( osgUtil::Tessellator::TESS_TYPE_GEOMETRY );
            tess.setWindingType( osgUtil::Tessellator::TESS_WINDING_POSITIVE );

            tess.retessellatePolygons( *osgGeom );

            // the tessellator results in a collection of trifans, strips, etc. This step will
            // consolidate those into one (or more if necessary) GL_TRIANGLES primitive.
            MeshConsolidator::run( *osgGeom );

            // mark this geometry as DYNAMIC because otherwise the OSG optimizer will destroy it.
            //osgGeom->setDataVariance( osg::Object::DYNAMIC );
        }

        if ( context.isGeocentric() && part->getType() != Geometry::TYPE_POINTSET )
        {
            double threshold = osg::DegreesToRadians( *_maxAngle_deg );

            MeshSubdivider ms( context.referenceFrame(), context.inverseReferenceFrame() );
            //ms.setMaxElementsPerEBO( INT_MAX );
            ms.run( threshold, *osgGeom );
        }

        // set the color array. We have to do this last, otherwise it screws up any modifications
        // make by the MeshSubdivider. No idea why. gw

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

        // NOTE! per-vertex colors makes the optimizer destroy the geometry....

        osg::Vec4Array* colors = new osg::Vec4Array(1);
        (*colors)[0] = color;
        osgGeom->setColorArray( colors );
        osgGeom->setColorBinding( osg::Geometry::BIND_OVERALL );

        // add the part to the geode.
        _geode->addDrawable( osgGeom );
    }
    
    return true;
}
Exemple #5
0
void
ScatterFilter::polyScatter(const Geometry*         input,
                           const SpatialReference* inputSRS,
                           const FilterContext&    context,
                           PointSet*               output ) const
{
    Bounds bounds;
    double areaSqKm = 0.0;

    ConstGeometryIterator iter( input, false );
    while( iter.hasMore() )
    {
        const Polygon* polygon = dynamic_cast<const Polygon*>( iter.next() );
        if ( !polygon )
            continue;

        if ( context.isGeocentric() || context.profile()->getSRS()->isGeographic() )
        {
            bounds = polygon->getBounds();

            double avglat = bounds.yMin() + 0.5*bounds.height();
            double h = bounds.height() * 111.32;
            double w = bounds.width() * 111.32 * sin( 1.57079633 + osg::DegreesToRadians(avglat) );

            areaSqKm = w * h;
        }

        else if ( context.profile()->getSRS()->isProjected() )
        {
            bounds = polygon->getBounds();
            areaSqKm = (0.001*bounds.width()) * (0.001*bounds.height());
        }

        double zMin = 0.0;
        unsigned numInstancesInBoundingRect = areaSqKm * (double)osg::clampAbove( 0.1f, _density );
        if ( numInstancesInBoundingRect == 0 )
            continue;

        if ( _random )
        {
            // Random scattering. Note, we try to place as many instances as would
            // fit in the bounding rectangle; The real placed number will be less since
            // we only place models inside the actual polygon. But the density will 
            // be correct.
            for( unsigned j=0; j<numInstancesInBoundingRect; ++j )
            {
                double rx = ((double)::rand()) / (double)RAND_MAX;
                double ry = ((double)::rand()) / (double)RAND_MAX;

                double x = bounds.xMin() + rx * bounds.width();
                double y = bounds.yMin() + ry * bounds.height();

                bool include = true;

                if ( include && polygon->contains2D( x, y ) )
                    output->push_back( osg::Vec3d(x, y, zMin) );
            }
        }

        else
        {
            // regular interval scattering:
            double numInst1D = sqrt((double)numInstancesInBoundingRect);
            double ar = bounds.width() / bounds.height();
            unsigned cols = (unsigned)( numInst1D * ar );
            unsigned rows = (unsigned)( numInst1D / ar );
            double colInterval = bounds.width() / (double)(cols-1);
            double rowInterval = bounds.height() / (double)(rows-1);
            double interval = 0.5*(colInterval+rowInterval);

            for( double cy=bounds.yMin(); cy<=bounds.yMax(); cy += interval )
            {
                for( double cx = bounds.xMin(); cx <= bounds.xMax(); cx += interval )
                {
                    bool include = true;

                    if ( include && polygon->contains2D( cx, cy ) )
                        output->push_back( osg::Vec3d(cx, cy, zMin) );
                }
            }
        }
    }
}
Exemple #6
0
FilterContext
ScatterFilter::push(FeatureList& features, const FilterContext& context )
{
    if ( !isSupported() ) {
        OE_WARN << LC << "support for this filter is not enabled" << std::endl;
        return context;
    }

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

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

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

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

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

            geomSRS = geomSRS->getGeographicSRS();
        }

        PointSet* points = new PointSet();

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

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

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

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

    return context;
}