osgEarth2.8加载矢量数据描边效果

通过修改osgearth自带的agglite插件,实现矢量描边效果,可以自定义描边的颜色和宽度(单位像素)

测试文件osgearth_features.cpp

#include <osg/Notify>
#include <osgGA/StateSetManipulator>
#include <osgViewer/Viewer>
#include <osgViewer/ViewerEventHandlers>
#include <osgEarth/Map>
#include <osgEarth/MapNode>
#include <osgEarthUtil/ExampleResources>
#include <osgEarthUtil/EarthManipulator>
#include <osgEarthUtil/AutoClipPlaneHandler>

#include <osgEarthSymbology/Style>
#include <osgEarthFeatures/ConvertTypeFilter>

#include <osgEarthDrivers/gdal/GDALOptions>
#include <osgEarthDrivers/feature_ogr/OGRFeatureOptions>
#include "AGGLiteOptions2"
#include <osgEarthDrivers/model_feature_geom/FeatureGeomModelOptions>

#include <osgDB/WriteFile>

#ifdef _DEBUG
#pragma comment(lib, "osgd.lib")
#pragma comment(lib, "osgDBd.lib")
#pragma comment(lib, "osgViewerd.lib")
#pragma comment(lib, "osgGAd.lib")
#pragma comment(lib, "osgEarthd.lib")
#pragma comment(lib, "osgEarthSymbologyd.lib")
#pragma comment(lib, "osgEarthUtild.lib")
#pragma comment(lib, "osgEarthFeaturesd.lib")
#else
#pragma comment(lib, "osg.lib")
#pragma comment(lib, "osgDB.lib")
#pragma comment(lib, "osgViewer.lib")
#pragma comment(lib, "osgGA.lib")
#pragma comment(lib, "osgEarth.lib")
#pragma comment(lib, "osgEarthSymbology.lib")
#pragma comment(lib, "osgEarthUtil.lib")
#pragma comment(lib, "osgEarthFeatures.lib")
#endif // DEBUG

using namespace osgEarth;
using namespace osgEarth::Features;
using namespace osgEarth::Drivers;
using namespace osgEarth::Symbology;
using namespace osgEarth::Util;

//
// NOTE: run this sample from the repo/tests directory.
//
int main(int argc, char** argv)
{
    osg::ArgumentParser arguments(&argc, argv);
    osgViewer::Viewer viewer(arguments);

    // Start by creating the map:
    Map* map = new Map();

    // Start with a basemap imagery layer; we'll be using the GDAL driver
    // to load a local GeoTIFF file:
    GDALOptions basemapOpt;
    basemapOpt.url() = "world.tif";
    map->addImageLayer(new ImageLayer(ImageLayerOptions("basemap", basemapOpt)));

    // Next we add a feature layer. 
    OGRFeatureOptions featureOptions;
    // Configures the feature driver to load the vectors from a shapefile:
    featureOptions.url() = "world.shp";

    

    // That's it, the map is ready; now create a MapNode to render the Map:
    MapNodeOptions mapNodeOptions;
    mapNodeOptions.enableLighting() = false;
    MapNode* mapNode = new MapNode(map, mapNodeOptions);

    osg::Group* root = new osg::Group();
    root->addChild(mapNode);
    viewer.setSceneData(root);
    viewer.setCameraManipulator(new EarthManipulator());

    // Process cmdline args
    MapNodeHelper().parse(mapNode, arguments, &viewer, root, new LabelControl("Features Demo"));

    // Define a style for the feature data. Since we are going to render the
    // vectors as lines, configure the line symbolizer:
    Style style;
    LineSymbol* ls = style.getOrCreateSymbol<LineSymbol>();
    ls->stroke()->color() = Color::Yellow;
    ls->stroke()->width() = 6.0f;
    ls->stroke()->lineCap() = osgEarth::Symbology::Stroke::LINECAP_ROUND;//使连接处圆滑一些

    AGGLiteOptions2 rasterOptions;
    rasterOptions.featureOptions() = featureOptions;
    rasterOptions.styles() = new StyleSheet();
    rasterOptions.styles()->addStyle(style);
    rasterOptions.borderColor() = Color::Red;//包边颜色
    rasterOptions.borderWidth() = 3.0f;//包边宽度(单位像素)
    map->addImageLayer(new ImageLayer("my features", rasterOptions));

    // add some stock OSG handlers:
    viewer.addEventHandler(new osgViewer::StatsHandler());
    viewer.addEventHandler(new osgViewer::WindowSizeHandler());
    viewer.addEventHandler(new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()));
    viewer.setUpViewInWindow(100, 500, 800, 600);
    return viewer.run();
}

 

头文件AGGLiteOptions2

#ifndef OSGEARTH_DRIVER_AGGLITE2_DRIVEROPTIONS
#define OSGEARTH_DRIVER_AGGLITE2_DRIVEROPTIONS 1

#include <osgEarth/Common>
#include <osgEarthFeatures/FeatureTileSource>

namespace osgEarth { namespace Drivers
{
    using namespace osgEarth;
    using namespace osgEarth::Features;

    class AGGLiteOptions2 : public FeatureTileSourceOptions // NO EXPORT; header only
    {
    public:
        /** 
         * Whether to downsample line features to that they are no higher resolution than
         * the target image resolution. Defaults to true, but you can disable this (for a possible
         * performance increase) if you know your data to be of a relatively low resolution.
         * (Default = true)
         */
        optional<bool>& optimizeLineSampling() { return _optimizeLineSampling; }
        const optional<bool>& optimizeLineSampling() const { return _optimizeLineSampling; }

        /** 
         * Set the gamma parameter applied on the AggLite rasterizer : allow to control geometry antialiasing
         * (Default = 1.3)
         */
        optional<double>& gamma() { return _gamma; }
        const optional<double>& gamma() const { return _gamma; }

        //w.g.描边的颜色和宽度(单位像素)
        /** Border color. */
        Color& borderColor() { return _borderColor; }
        const Color& borderColor() const { return _borderColor; }
        /** Line border rendering width. */
        optional<double>& borderWidth() { return _borderWidth; }
        const optional<double>& borderWidth() const { return _borderWidth; }

    public:
        AGGLiteOptions2( const TileSourceOptions& options =TileSourceOptions() )
            : FeatureTileSourceOptions( options ),
              _optimizeLineSampling   ( true ),
              _gamma                  ( 1.3 ),
            _borderColor(Color::White),
            _borderWidth(1)
        {
            setDriver( "agglite2" );
            fromConfig( _conf );
        }

        /** dtor */
        virtual ~AGGLiteOptions2() { }

    public:
        Config getConfig() const {
            Config conf = FeatureTileSourceOptions::getConfig();
            conf.updateIfSet("optimize_line_sampling", _optimizeLineSampling);
            conf.updateIfSet("gamma", _gamma );
            conf.add("borderColor", _borderColor.toHTML());
            conf.updateIfSet("borderWidth", _borderWidth);
            return conf;
        }

    protected:
        void mergeConfig( const Config& conf ) {
            FeatureTileSourceOptions::mergeConfig( conf );
            fromConfig(conf);
        }

    private:
        void fromConfig( const Config& conf ) {
            conf.getIfSet( "optimize_line_sampling", _optimizeLineSampling );
            conf.getIfSet( "gamma", _gamma );
            _borderColor = Color(conf.value("borderColor"));
            conf.getIfSet("borderWidth", _borderWidth);
        }

        optional<bool>   _optimizeLineSampling;
        optional<double> _gamma;
        //w.g.描边的颜色和宽度(单位像素)
        Color            _borderColor;
        optional<double>  _borderWidth;
    };

} } // namespace osgEarth::Drivers

#endif // OSGEARTH_DRIVER_AGGLITE2_DRIVEROPTIONS

插件文件AGGLiteRasterizerTileSource2.cpp

#include <osgEarthFeatures/FeatureTileSource>
#include <osgEarthFeatures/ResampleFilter>
#include <osgEarthFeatures/TransformFilter>
#include <osgEarthFeatures/BufferFilter>
#include <osgEarthSymbology/Style>
//TODO: replace this with GeometryRasterizer
#include <osgEarthSymbology/AGG.h>
#include <osgEarth/Registry>
#include <osgEarth/FileUtils>
#include <osgEarth/ImageUtils>

#include <osg/Notify>
#include <osgDB/FileNameUtils>
#include <osgDB/FileUtils>
#include <osgDB/Registry>
#include <osgDB/ReadFile>
#include <osgDB/WriteFile>

#include "AGGLiteOptions2"

#include <sstream>
#include <OpenThreads/Mutex>
#include <OpenThreads/ScopedLock>

#define LC "[AGGLite2] "

using namespace osgEarth;
using namespace osgEarth::Features;
using namespace osgEarth::Symbology;
using namespace osgEarth::Drivers;
using namespace OpenThreads;

namespace
{
    struct float32
    {
        float32() : value(NO_DATA_VALUE) { }
        float32(float v) : value(v) { }

        float value;
    };

    struct span_coverage32
    {
        static void render(unsigned char* ptr, 
                           int x,
                           unsigned count, 
                           const unsigned char* covers, 
                           const float32& c)
        {
            unsigned char* p = ptr + (x << 2);
            float* f = (float*)p;
            do
            {
                unsigned char cover = *covers++;
                int hasData = cover > 127;
                *f++ = hasData ? c.value : NO_DATA_VALUE;
            }
            while(--count);
        }

        static void hline(unsigned char* ptr, 
                          int x,
                          unsigned count, 
                          const float32& c)
        {
            unsigned char* p = ptr + (x << 2);
            float* f = (float*)p;
            do {
                *f++ = c.value;
            }
            while(--count);
        }

        static float32 get(unsigned char* ptr, int x)
        {
            unsigned char* p = ptr + (x << 2);
            float* f = (float*)p;
            return float32(*f);
        }
    };
}

/********************************************************************/

class AGGLiteRasterizerTileSource2 : public FeatureTileSource
{
public:
    struct RenderFrame {
        double xmin, ymin;
        double xf, yf;
    };

public:
    AGGLiteRasterizerTileSource2( const TileSourceOptions& options ) : FeatureTileSource( options ),
        _options( options )
    {
        //nop
    }

    //override
    osg::Image* allocateImage()
    {
        osg::Image* image = 0L;
        if ( _options.coverage() == true )
        {
            image = new osg::Image();
            image->allocateImage(getPixelsPerTile(), getPixelsPerTile(), 1, GL_LUMINANCE, GL_FLOAT);
            image->setInternalTextureFormat(GL_LUMINANCE32F_ARB);
            ImageUtils::markAsUnNormalized(image, true);
        }
        return image;
    }

    //override
    bool preProcess(osg::Image* image, osg::Referenced* buildData)
    {
        agg::rendering_buffer rbuf( image->data(), image->s(), image->t(), image->s()*4 );

        // clear the buffer.
        if ( _options.coverage() == true )
        {
            // For coverage data, FLT_MAX = no data.
            agg::renderer<span_coverage32, float32> ren(rbuf);
            ren.clear( float32(NO_DATA_VALUE) );
        }
        else
        {
            agg::renderer<agg::span_abgr32, agg::rgba8> ren(rbuf);
            ren.clear(agg::rgba8(0,0,0,0));
        }
        return true;
    }

    //override
    bool renderFeaturesForStyle(
        Session*           session,
        const Style&       style,
        const FeatureList& features,
        osg::Referenced*   buildData,
        const GeoExtent&   imageExtent,
        osg::Image*        image )
    {
        //w.g.先绘制描边
        renderFeaturesForStyle2(session, style, features, buildData, imageExtent, image);

        OE_DEBUG << LC << "Rendering " << features.size() << " features for " << imageExtent.toString() << "\n";

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

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

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

        for(FeatureList::const_iterator f = features.begin(); f != features.end(); ++f)
        {
            if ( f->get()->getGeometry() )
            {
                bool hasPoly = false;
                bool hasLine = false;

                if ( masterPoly || f->get()->style()->has<PolygonSymbol>() )
                {
                    polygons.push_back( f->get() );
                    hasPoly = true;
                }

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

                // if there are no geometry symbols but there is a coverage symbol, default to polygons.
                if ( !hasLine && !hasPoly )
                {
                    if ( masterCov || f->get()->style()->has<CoverageSymbol>() )
                    {
                        polygons.push_back( f->get() );
                    }
                }
            }
        }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        // Create the renderer and the rasterizer
        agg::rasterizer ras;

        // Setup the rasterizer
        if ( _options.coverage() == true )
            ras.gamma(1.0);
        else
            ras.gamma(_options.gamma().get());

        ras.filling_rule(agg::fill_even_odd);

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

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

        // If there's a coverage symbol, make a copy of the expressions so we can evaluate them
        optional<NumericExpression> covValue;
        const CoverageSymbol* covsym = style.get<CoverageSymbol>();
        if (covsym && covsym->valueExpression().isSet())
            covValue = covsym->valueExpression().get();

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

            osg::ref_ptr<Geometry> croppedGeometry;
            if ( geometry->crop( cropPoly.get(), croppedGeometry ) )
            {
                const PolygonSymbol* poly =
                    feature->style().isSet() && feature->style()->has<PolygonSymbol>() ? feature->style()->get<PolygonSymbol>() :
                    masterPoly;

                if ( _options.coverage() == true && covValue.isSet() )
                {
                    float value = (float)feature->eval(covValue.mutable_value(), &context);
                    rasterizeCoverage(croppedGeometry.get(), value, frame, ras, rbuf);
                }
                else
                {
                    osg::Vec4f color = poly->fill()->color();
                    rasterize(croppedGeometry.get(), color, frame, ras, rbuf);
                }
                
            }
        }

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

            osg::ref_ptr<Geometry> croppedGeometry;
            if ( geometry->crop( cropPoly.get(), croppedGeometry ) )
            {
                const LineSymbol* line =
                    feature->style().isSet() && feature->style()->has<LineSymbol>() ? feature->style()->get<LineSymbol>() :
                    masterLine;

                if ( _options.coverage() == true && covValue.isSet() )
                {
                    float value = (float)feature->eval(covValue.mutable_value(), &context);
                    rasterizeCoverage(croppedGeometry.get(), value, frame, ras, rbuf);
                }
                else
                {   osg::Vec4f color = line ? static_cast<osg::Vec4>(line->stroke()->color()) : osg::Vec4(1,1,1,1);
                    rasterize(croppedGeometry.get(), color, frame, ras, rbuf);
                }
            }
        }

        return true;
    }

    //w.g.描边
    bool renderFeaturesForStyle2(
        Session*           session,
        const Style&       style,
        const FeatureList& features,
        osg::Referenced*   buildData,
        const GeoExtent&   imageExtent,
        osg::Image*        image)
    {
        OE_DEBUG << LC << "Rendering " << features.size() << " features for " << imageExtent.toString() << "\n";

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

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

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

        for (FeatureList::const_iterator f = features.begin(); f != features.end(); ++f)
        {
            if (f->get()->getGeometry())
            {
                bool hasPoly = false;
                bool hasLine = false;

                if (masterPoly || f->get()->style()->has<PolygonSymbol>())
                {
                    polygons.push_back(f->get());
                    hasPoly = true;
                }

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

                // if there are no geometry symbols but there is a coverage symbol, default to polygons.
                if (!hasLine && !hasPoly)
                {
                    if (masterCov || f->get()->style()->has<CoverageSymbol>())
                    {
                        polygons.push_back(f->get());
                    }
                }
            }
        }

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

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

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

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

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

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

                if (masterLine->stroke()->width().isSet())
                {
                    //w.g.描边的宽度(单位像素)
                    lineWidth = masterLine->stroke()->width().value()+_options.borderWidth().value()*2;

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

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

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

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

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

            buffer.distance() = lineWidth*0.5;
            buffer.push(lines, context);
        }

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

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

        // Create the renderer and the rasterizer
        agg::rasterizer ras;

        // Setup the rasterizer
        if (_options.coverage() == true)
            ras.gamma(1.0);
        else
            ras.gamma(_options.gamma().get());

        ras.filling_rule(agg::fill_even_odd);

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

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

        // If there's a coverage symbol, make a copy of the expressions so we can evaluate them
        optional<NumericExpression> covValue;
        const CoverageSymbol* covsym = style.get<CoverageSymbol>();
        if (covsym && covsym->valueExpression().isSet())
            covValue = covsym->valueExpression().get();

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

            osg::ref_ptr<Geometry> croppedGeometry;
            if (geometry->crop(cropPoly.get(), croppedGeometry))
            {
                const PolygonSymbol* poly =
                    feature->style().isSet() && feature->style()->has<PolygonSymbol>() ? feature->style()->get<PolygonSymbol>() :
                    masterPoly;

                if (_options.coverage() == true && covValue.isSet())
                {
                    float value = (float)feature->eval(covValue.mutable_value(), &context);
                    rasterizeCoverage(croppedGeometry.get(), value, frame, ras, rbuf);
                }
                else
                {
                    osg::Vec4f color = poly->fill()->color();
                    rasterize(croppedGeometry.get(), color, frame, ras, rbuf);
                }

            }
        }

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

            osg::ref_ptr<Geometry> croppedGeometry;
            if (geometry->crop(cropPoly.get(), croppedGeometry))
            {
                const LineSymbol* line =
                    feature->style().isSet() && feature->style()->has<LineSymbol>() ? feature->style()->get<LineSymbol>() :
                    masterLine;

                if (_options.coverage() == true && covValue.isSet())
                {
                    float value = (float)feature->eval(covValue.mutable_value(), &context);
                    rasterizeCoverage(croppedGeometry.get(), value, frame, ras, rbuf);
                }
                else
                {
                    //w.g.描边的颜色
                    osg::Vec4f color = static_cast<osg::Vec4>(_options.borderColor());
                    rasterize(croppedGeometry.get(), color, frame, ras, rbuf);
                }
            }
        }

        return true;
    }
    //override
    bool postProcess( osg::Image* image, osg::Referenced* data )
    {
        if ( _options.coverage() == false )
        {
            //convert from ABGR to RGBA
            unsigned char* pixel = image->data();
            for(int i=0; i<image->s()*image->t()*4; i+=4, pixel+=4)
            {
                std::swap( pixel[0], pixel[3] );
                std::swap( pixel[1], pixel[2] );
            }
        }

        return true;
    }

    // rasterizes a geometry to color
    void rasterize(const Geometry* geometry, const osg::Vec4& color, RenderFrame& frame, 
                   agg::rasterizer& ras, agg::rendering_buffer& buffer)
    {
        unsigned a = (unsigned)(127.0f+(color.a()*255.0f)/2.0f); // scale alpha up
        agg::rgba8 fgColor = agg::rgba8( (unsigned)(color.r()*255.0f), (unsigned)(color.g()*255.0f), (unsigned)(color.b()*255.0f), a );
        
        ConstGeometryIterator gi( geometry );
        while( gi.hasMore() )
        {
            const Geometry* g = gi.next();

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

                if ( p == g->begin() )
                    ras.move_to_d( x0, y0 );
                else
                {
                    ras.line_to_d(x0, y0);
                }
            }
        }
        agg::renderer<agg::span_abgr32, agg::rgba8> ren(buffer);
        ras.render(ren, fgColor);

        ras.reset();
    }

    
    void rasterizeCoverage(const Geometry* geometry, float value, RenderFrame& frame, 
                           agg::rasterizer& ras, agg::rendering_buffer& buffer)
    {
        ConstGeometryIterator gi( geometry );
        while( gi.hasMore() )
        {
            const Geometry* g = gi.next();

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

                if ( p == g->begin() )
                    ras.move_to_d( x0, y0 );
                else
                    ras.line_to_d( x0, y0 );
            }
        }
        
        agg::renderer<span_coverage32, float32> ren(buffer);
        ras.render(ren, value);
        ras.reset();
    }


    virtual std::string getExtension()  const 
    {
        return "png";
    }

private:
    const AGGLiteOptions2 _options;
    std::string _configPath;
};


/**
 * Plugin entry point for the AGGLite2 feature rasterizer
 */
class AGGLiteRasterizerTileSourceDriver2 : public TileSourceDriver
{
    public:
        AGGLiteRasterizerTileSourceDriver2() {}

        virtual const char* className() const
        {
            return "AGG-Lite2 feature rasterizer";
        }
        
        virtual bool acceptsExtension(const std::string& extension) const
        {
            return
                osgDB::equalCaseInsensitive( extension, "osgearth_agglite2" ) ||
                osgDB::equalCaseInsensitive( extension, "osgearth_rasterize2" );
        }

        virtual ReadResult readObject(const std::string& file_name, const Options* options) const
        {
            std::string ext = osgDB::getFileExtension( file_name );
            if ( !acceptsExtension( ext ) )
            {
                return ReadResult::FILE_NOT_HANDLED;
            }

            return new AGGLiteRasterizerTileSource2( getTileSourceOptions(options) );
        }
};

REGISTER_OSGPLUGIN(osgearth_agglite2, AGGLiteRasterizerTileSourceDriver2)

 

posted @ 2017-07-28 17:19  酷熊  阅读(2408)  评论(0编辑  收藏  举报