void test_ImageOverlay::split_ShouldReturnExpectedResults_data()
{
    QList<ImageOverlay> overlays = ImageOverlayTestHelper::createImageOverlays();
    QList< QList<ImageOverlay> > overlaysSubOverlays = ImageOverlayTestHelper::createSubOverlays();

    QTest::addColumn<ImageOverlay>("overlay");
    QTest::addColumn< QList<ImageOverlay> >("subOverlays");

    ImageOverlay overlay;

    QTest::newRow("invalid overlay") << overlay << QList<ImageOverlay>();

    overlay.setRows(128);
    overlay.setColumns(128);
    overlay.setData(new unsigned char[128 * 128]);
    memset(overlay.getData(), 0, 128 * 128 * sizeof(unsigned char));
    QTest::newRow("empty overlay") << overlay << QList<ImageOverlay>();

    QTest::newRow("overlay #1") << overlays[0] << overlaysSubOverlays[0];
    // overlays[1] és per quan no optimitzem per potències de 2
    //QTest::newRow("overlay #2") << overlays[1] << overlaysSubOverlays[1];
    QTest::newRow("overlay #3") << overlays[2] << overlaysSubOverlays[2];
    QTest::newRow("overlay #4") << overlays[3] << overlaysSubOverlays[3];
    // overlays[4] és per quan sí optimitzem per potències de 2
    QTest::newRow("overlay #5") << overlays[4] << overlaysSubOverlays[4];
}
void test_ImageOverlay::createSubOverlay_ShouldReturnInvalidOverlay_data()
{
    QTest::addColumn<ImageOverlay>("overlay");
    QTest::addColumn<QRect>("region");

    ImageOverlay overlay;

    overlay.setRows(5);
    overlay.setColumns(5);
    QTest::newRow("invalid overlay, region not inside") << overlay << QRect(1, 2, 7, 3);
    QTest::newRow("invalid overlay, region inside") << overlay << QRect(1, 1, 1, 1);

    overlay.setData(new unsigned char[1]);
    QTest::newRow("valid overlay, region not inside") << overlay << QRect(1, 2, 7, 3);
}
void test_ImageOverlay::fromGDCMOverlay_ReturnsExpectedValues_data()
{
    QTest::addColumn<gdcm::Overlay>("gdcmOverlay");
    QTest::addColumn<ImageOverlay>("imageOverlay");

    gdcm::Overlay emptyGDCMOverlay;
    ImageOverlay imageOverlayLikeEmptyGDCMOverlay;
    imageOverlayLikeEmptyGDCMOverlay.setOrigin(0, 0);
    QTest::newRow("empty gdcm overlay") << emptyGDCMOverlay << imageOverlayLikeEmptyGDCMOverlay;

    int rows = 5;
    int columns = 7;
    short origin[2] = { 22, -12 };
   
    const char *gdcmBuffer = new char[rows * columns];
    
    gdcm::Overlay overlayWithData;
    overlayWithData.SetColumns(columns);
    overlayWithData.SetRows(rows);
    overlayWithData.SetOrigin(origin);
    overlayWithData.SetOverlay(gdcmBuffer, rows * columns);

    size_t bufferSize = overlayWithData.GetUnpackBufferLength();
    unsigned char *imageOverlayBuffer = new unsigned char[bufferSize];
    overlayWithData.GetUnpackBuffer(reinterpret_cast<char*>(imageOverlayBuffer), bufferSize);
    
    ImageOverlay imageOverlay;
    imageOverlay.setRows(rows);
    imageOverlay.setColumns(columns);
    imageOverlay.setOrigin(origin[0], origin[1]);
    imageOverlay.setData(imageOverlayBuffer);

    QTest::newRow("gdcm overlay with data") << overlayWithData << imageOverlay;
}
Exemple #4
0
void LibraryMenuItem::init(QSharedPointer<Library> &library)
{
	_library = library;
	_closeButton = NULL;

	QString fontFamily = themeManager->getValueAsFontFamilyName(QT_NT("ui.message.font.family"),QT_NT(""));
	int fontSize = themeManager->getValueAsInt(QT_NT("ui.message.font.size"), 14);
	
	TextOverlay* text = new TextOverlay(_library->getName());
	text->getStyle().setAlpha(1.0f);
	text->setFont(FontDescription(fontFamily, fontSize));
	text->getStyle().setBackgroundColor(ColorVal(0, 0, 0, 0));
	text->getStyle().setPadding(LeftRightEdges, 0.0f);
	text->getStyle().setPadding(TopBottomEdges, 0.0f);

	float height = (float)text->getTextBuffer().getActualSize().height();
	ImageOverlay* icon = new ImageOverlay(_library->getIconTextureKey());
	icon->getStyle().setScaledDimensions(Vec3(height, height, 0.0f));

	this->addItem(icon);
	this->addItem(text);
}
void test_ImageOverlay::ImageOverlay_InitializesClassAsExpected()
{
    ImageOverlay overlay;
    QCOMPARE(overlay.getRows(), 0);
    QCOMPARE(overlay.getColumns(), 0);
    QCOMPARE(overlay.getXOrigin(), 1);
    QCOMPARE(overlay.getYOrigin(), 1);
    unsigned char *nullUCharPointer = 0;
    QCOMPARE(overlay.getData(), nullUCharPointer);
}
int
main(int argc, char** argv)
{
    osg::ArgumentParser arguments(&argc,argv);
    osg::DisplaySettings::instance()->setMinimumNumStencilBits( 8 );


    std::vector< std::string > imageFiles;
    std::vector< Bounds > imageBounds;

    //Read in the image files
    std::string filename;
    Bounds bounds;
    while (arguments.read("--image", filename, bounds.xMin(), bounds.yMin(), bounds.xMax(), bounds.yMax()))
    {
        imageFiles.push_back( filename );
        imageBounds.push_back( bounds );
    }

    if (imageFiles.empty())
    {
      imageFiles.push_back("../data/osgearth.gif");
      imageBounds.push_back( Bounds(-100, 30, -90, 40) );
    }
 

    bool moveVert = arguments.read("--vert");

    // load the .earth file from the command line.
    osg::Node* earthNode = osgDB::readNodeFiles( arguments );
    if (!earthNode)
        return usage( "Unable to load earth model." );

    osgViewer::Viewer viewer(arguments);
    
    EarthManipulator* manip = new EarthManipulator();
    viewer.setCameraManipulator( manip );

    osg::Group* root = new osg::Group();
    root->addChild( earthNode );

    //Create the control panel
    root->addChild( createControlPanel(&viewer) );

    viewer.setSceneData( root );
    
    osgEarth::MapNode* mapNode = osgEarth::MapNode::findMapNode( earthNode );
    if ( mapNode )
    {

        for (unsigned int i = 0; i < imageFiles.size(); i++)
        {
            std::string imageFile = imageFiles[i];
            //Read the image file and play it if it's a movie
            osg::Image* image = osgDB::readImageFile(imageFile);
            if (image)
            {
                osg::ImageStream* is = dynamic_cast<osg::ImageStream*>(image);
                if (is)
                {
                    is->play();
                }
            }

            //Create a new ImageOverlay and set it's bounds
            //ImageOverlay* overlay = new ImageOverlay(mapNode->getMap()->getProfile()->getSRS()->getEllipsoid(), image);        
            ImageOverlay* overlay = new ImageOverlay(mapNode);
            overlay->setImage( image );
            overlay->setBounds(imageBounds[i]);

            root->addChild( overlay );


            //Create a new ImageOverlayEditor and set it's node mask to 0 to hide it initially
#if OSG_MIN_VERSION_REQUIRED(2,9,6)
            osg::Node* editor = new ImageOverlayEditor( overlay);
#else
            //Just make an empty group for pre-2.9.6
            osg::Node* editor = new osg::Group;
#endif
            editor->setNodeMask( 0 );
            root->addChild( editor );      
            
            // Add an image preview
            ImageControl* imageCon = new ImageControl( image );
            imageCon->setSize( 64, 64 );
            imageCon->setVertAlign( Control::ALIGN_CENTER );
            s_layerBox->setControl( 0, i, imageCon );            


            //Add some controls        
            CheckBoxControl* enabled = new CheckBoxControl( true );
            enabled->addEventHandler( new EnabledHandler(overlay) );
            enabled->setVertAlign( Control::ALIGN_CENTER );
            s_layerBox->setControl( 1, i, enabled );

            //The overlay name
            LabelControl* name = new LabelControl( osgDB::getSimpleFileName( imageFile) );      
            name->setVertAlign( Control::ALIGN_CENTER );
            s_layerBox->setControl( 2, i, name );

            // an opacity slider
            HSliderControl* opacity = new HSliderControl( 0.0f, 1.0f, overlay->getAlpha() );
            opacity->setWidth( 125 );
            opacity->setHeight( 12 );
            opacity->setVertAlign( Control::ALIGN_CENTER );
            opacity->addEventHandler( new OpacityHandler(overlay) );
            s_layerBox->setControl( 3, i, opacity );

            // Add a text label:
            LabelControl* edit = new LabelControl( "Edit" );        
            edit->setVertAlign( Control::ALIGN_CENTER );
            edit->addEventHandler(new EditHandler(overlay, &viewer, editor));
            s_layerBox->setControl(4, i, edit );
        }        
    }

    // osgEarth benefits from pre-compilation of GL objects in the pager. In newer versions of
    // OSG, this activates OSG's IncrementalCompileOpeartion in order to avoid frame breaks.
    viewer.getDatabasePager()->setDoPreCompile( true );

    // add some stock OSG handlers:
    viewer.addEventHandler(new osgViewer::StatsHandler());
    viewer.addEventHandler(new osgViewer::WindowSizeHandler());    
    viewer.addEventHandler(new osgViewer::LODScaleHandler());
    viewer.addEventHandler(new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()));
    viewer.addEventHandler(new osgViewer::HelpHandler(arguments.getApplicationUsage()));

    return viewer.run();
}
void test_ImageOverlay::isValid_ReturnsExpectedValue_data()
{
    QTest::addColumn<ImageOverlay>("imageOverlay");
    QTest::addColumn<bool>("expectedValue");

    ImageOverlay imageOverlay;

    imageOverlay.setRows(0);
    imageOverlay.setColumns(0);
    imageOverlay.setData(0);
    QTest::newRow("no rows, no columns, no data") << imageOverlay << false;

    imageOverlay.setRows(0);
    imageOverlay.setColumns(0);
    imageOverlay.setData(new unsigned char[1]);
    QTest::newRow("no rows, no columns, data") << imageOverlay << false;

    imageOverlay.setRows(0);
    imageOverlay.setColumns(1);
    imageOverlay.setData(0);
    QTest::newRow("no rows, columns, no data") << imageOverlay << false;

    imageOverlay.setRows(0);
    imageOverlay.setColumns(2);
    imageOverlay.setData(new unsigned char[1]);
    QTest::newRow("no rows, columns, data") << imageOverlay << false;

    imageOverlay.setRows(3);
    imageOverlay.setColumns(0);
    imageOverlay.setData(0);
    QTest::newRow("rows, no columns, no data") << imageOverlay << false;

    imageOverlay.setRows(4);
    imageOverlay.setColumns(0);
    imageOverlay.setData(new unsigned char[1]);
    QTest::newRow("rows, no columns, data") << imageOverlay << false;

    imageOverlay.setRows(5);
    imageOverlay.setColumns(6);
    imageOverlay.setData(0);
    QTest::newRow("rows, columns, no data") << imageOverlay << false;

    imageOverlay.setRows(7);
    imageOverlay.setColumns(8);
    imageOverlay.setData(new unsigned char[1]);
    QTest::newRow("rows, columns, data") << imageOverlay << true;
}
void test_ImageOverlay::mergeOverlays_ReturnsExpectedImageOverlay_data()
{
    QTest::addColumn<QList<ImageOverlay> >("overlaysList");
    QTest::addColumn<ImageOverlay>("mergedOverlay");
    QTest::addColumn<bool>("mergeWasSuccessful");

    QList<ImageOverlay> overlaysList;
    QTest::newRow("Empty List") << overlaysList << ImageOverlay() << true;

    ImageOverlay overlay1;
    overlay1.setColumns(512);
    overlay1.setRows(512);
    overlay1.setOrigin(1, 1);
    unsigned char *data1 = new unsigned char[512 * 512];
    memset(data1, 0, sizeof(unsigned char) * 512 * 512);
    memset(data1, 255, sizeof(unsigned char) * 512);
    overlay1.setData(data1);
    
    overlaysList.clear();
    overlaysList << overlay1;
    QTest::newRow("Single element list") << overlaysList << overlay1 << true;

    overlaysList.clear();
    overlaysList << overlay1 << overlay1;
    QTest::newRow("2 elements, same data") << overlaysList << overlay1 << true;
    
    ImageOverlay overlay2 = overlay1;
    unsigned char *data2 = new unsigned char[512 * 512];
    memset(data2, 0, sizeof(unsigned char) * 512 * 512);
    memset(data2 + sizeof(unsigned char) * 512, 255, sizeof(unsigned char) * 512);
    overlay2.setData(data2);

    ImageOverlay mergedOverlay;
    mergedOverlay.setColumns(512);
    mergedOverlay.setRows(512);
    mergedOverlay.setOrigin(1, 1);
    unsigned char *mergedData1 = new unsigned char[512 * 512];
    memset(mergedData1, 0, sizeof(unsigned char) * 512 * 512);
    memset(mergedData1, 255, sizeof(unsigned char) * 1024);
    mergedOverlay.setData(mergedData1);
    
    overlaysList.clear();
    overlaysList << overlay1 << overlay2;
    QTest::newRow("2 overlays, same origin, same cols x rows, different data") << overlaysList << mergedOverlay << true;

    ImageOverlay overlay3;
    overlay3.setColumns(512);
    overlay3.setRows(512);
    overlay3.setOrigin(-10, 5);
    unsigned char *data3 = new unsigned char[512 * 512];
    memset(data3, 0, sizeof(unsigned char) * 512 * 512);
    memset(data3, 255, sizeof(unsigned char) * 512);
    overlay3.setData(data3);

    ImageOverlay mergedOverlay1_3;
    mergedOverlay1_3.setColumns(512 + 11);
    mergedOverlay1_3.setRows(512 + 4);
    mergedOverlay1_3.setOrigin(-10, 1);
    int dataLength = mergedOverlay1_3.getColumns() * mergedOverlay1_3.getRows();
    unsigned char *mergedData1_3 = new unsigned char[dataLength];
    memset(mergedData1_3, 0, sizeof(unsigned char) * dataLength);
    memset(mergedData1_3 + sizeof(unsigned char) * 11, 255, sizeof(unsigned char) * 512);
    memset(mergedData1_3 + sizeof(unsigned char) * mergedOverlay1_3.getColumns() * 4, 255, sizeof(unsigned char) * 512);
    mergedOverlay1_3.setData(mergedData1_3);
    
    overlaysList.clear();
    overlaysList << overlay1 << overlay3;
    QTest::newRow("2 overlays, different origin, same cols x rows") << overlaysList << mergedOverlay1_3 << true;

    ImageOverlay overlay4;
    overlay4.setColumns(10);
    overlay4.setRows(10);
    overlay4.setOrigin(1, 1);
    unsigned char *data4 = new unsigned char[10 * 10];
    memset(data4, 0, sizeof(unsigned char) * 100);
    memset(data4, 255, sizeof(unsigned char) * 10);
    overlay4.setData(data4);
    
    ImageOverlay overlay5;
    overlay5.setColumns(11);
    overlay5.setRows(11);
    overlay5.setOrigin(1, 1);
    unsigned char *data5 = new unsigned char[11 * 11];
    memset(data5, 0, sizeof(unsigned char) * 121);
    memset(data5 + sizeof(unsigned char) * 11 * 10, 255, sizeof(unsigned char) * 11);
    overlay5.setData(data5);

    ImageOverlay mergedOverlay4_5;
    mergedOverlay4_5.setColumns(11);
    mergedOverlay4_5.setRows(11);
    mergedOverlay4_5.setOrigin(1, 1);
    unsigned char *mergedData4_5 = new unsigned char[11 * 11];
    memset(mergedData4_5, 0, sizeof(unsigned char) * 121);
    memset(mergedData4_5, 255, sizeof(unsigned char) * 10);
    memset(mergedData4_5 + sizeof(unsigned char) * 11 * 10, 255, sizeof(unsigned char) * 11);
    mergedOverlay4_5.setData(mergedData4_5);
    
    overlaysList.clear();
    overlaysList << overlay4 << overlay5;
    QTest::newRow("2 overlays, same origin, different cols x rows") << overlaysList << mergedOverlay4_5 << true;

    ImageOverlay overlay6;
    overlay6.setColumns(8);
    overlay6.setRows(6);
    overlay6.setOrigin(2, -10);
    unsigned char *data6 = new unsigned char[6 * 8];
    memset(data6, 0, sizeof(unsigned char) * 48);
    data6[0] = 255;
    data6[13] = 255;
    data6[42] = 255;
    overlay6.setData(data6);
    
    ImageOverlay overlay7;
    overlay7.setColumns(3);
    overlay7.setRows(20);
    overlay7.setOrigin(-6, -7);
    unsigned char *data7 = new unsigned char[3 * 20];
    memset(data7, 0, sizeof(unsigned char) * 60);
    data7[0] = 255;
    data7[1] = 255;
    data7[31] = 255;
    data7[59] = 255;
    overlay7.setData(data7);

    ImageOverlay mergedOverlay6_7;
    mergedOverlay6_7.setColumns(16);
    mergedOverlay6_7.setRows(23);
    mergedOverlay6_7.setOrigin(-6, -10);
    unsigned char *mergedData6_7 = new unsigned char[23 * 16];
    memset(mergedData6_7, 0, sizeof(unsigned char) * 23 * 16);
    mergedData6_7[8] = 255;
    mergedData6_7[29] = 255;
    mergedData6_7[48] = 255;
    mergedData6_7[49] = 255;
    mergedData6_7[90] = 255;
    mergedData6_7[209] = 255;
    mergedData6_7[354] = 255;
    mergedOverlay6_7.setData(mergedData6_7);
    
    overlaysList.clear();
    overlaysList << overlay6 << overlay7;
    QTest::newRow("2 overlays, different origin, different cols x rows") << overlaysList << mergedOverlay6_7 << true;

    ImageOverlay nullRowsOverlay;
    nullRowsOverlay.setColumns(512);
    nullRowsOverlay.setRows(0);
    nullRowsOverlay.setOrigin(3, 25);

    overlaysList.clear();
    overlaysList << overlay1 << nullRowsOverlay;
    QTest::newRow("2 overlays, one invalid (no rows)") << overlaysList << overlay1 << true;

    ImageOverlay nullColumnsOverlay;
    nullColumnsOverlay.setColumns(0);
    nullColumnsOverlay.setRows(128);
    nullColumnsOverlay.setOrigin(2, 5);

    overlaysList.clear();
    overlaysList << overlay1 << nullColumnsOverlay;
    QTest::newRow("2 overlays, one invalid (no columns)") << overlaysList << overlay1 << true;

    ImageOverlay nullDataOverlay;
    nullDataOverlay.setColumns(128);
    nullDataOverlay.setRows(128);
    nullDataOverlay.setOrigin(2, 5);
    nullDataOverlay.setData(0);

    overlaysList.clear();
    overlaysList << overlay1 << nullDataOverlay;
    QTest::newRow("2 overlays, one invalid (no data)") << overlaysList << overlay1 << true;

    overlaysList.clear();
    overlaysList << ImageOverlay() << ImageOverlay();
    QTest::newRow("2 empty overlays") << overlaysList << ImageOverlay() << true;

    overlaysList.clear();
    overlaysList << nullRowsOverlay;
    QTest::newRow("1 invalid overlay (no rows)") << overlaysList << ImageOverlay() << true;

    overlaysList.clear();
    overlaysList << nullColumnsOverlay;
    QTest::newRow("1 invalid overlay (no columns)") << overlaysList << ImageOverlay() << true;

    overlaysList.clear();
    overlaysList << nullDataOverlay;
    QTest::newRow("1 invalid overlay (no data)") << overlaysList << ImageOverlay() << true;

    overlaysList.clear();
    overlaysList << nullColumnsOverlay << nullRowsOverlay << nullDataOverlay;
    QTest::newRow("3 invalid overlays (no rows, no columns, no data)") << overlaysList << ImageOverlay() << true;
}
Exemple #9
0
void
KML_GroundOverlay::build( const Config& conf, KMLContext& cx )
{
    // the URL of the overlay image
    std::string href = conf.child("icon").value("href");
    if ( href.empty() ) {
        OE_WARN << LC << "GroundOverlay missing required Icon element" << std::endl;
        return;
    }

    ImageOverlay* im = 0L;

    // the extent of the overlay image
    const Config& llb = conf.child("latlonbox");
    if ( !llb.empty() )
    {
        double north = llb.value<double>("north", 0.0);
        double south = llb.value<double>("south", 0.0);
        double east  = llb.value<double>("east", 0.0);
        double west  = llb.value<double>("west", 0.0);
        Angular rotation( -llb.value<double>("rotation", 0.0), Units::DEGREES );

        osg::ref_ptr<osg::Image> image = URI(href, conf.referrer()).readImage().getImage();
        if ( !image.valid() )
        {
            OE_WARN << LC << "GroundOverlay failed to read image from " << href << std::endl;
            return;
        }

        im = new ImageOverlay( cx._mapNode, image.get() );
        im->setBoundsAndRotation( Bounds(west, south, east, north), rotation );
        cx._groupStack.top()->addChild( im );
    }

    else if ( conf.hasChild("gx:latlonquad") )
    {
        const Config& llq = conf.child("gx:latlonquad");
        KML_Geometry g;
        Style style;
        g.buildChild( llq, cx, style );
        if ( g._geom.valid() && g._geom->size() >= 4 )
        {
            osg::ref_ptr<osg::Image> image = URI(href, conf.referrer()).readImage().getImage();
            if ( !image.valid() )
            {
                OE_WARN << LC << "GroundOverlay failed to read image from " << href << std::endl;
                return;
            }

            const Geometry& p = *(g._geom.get());
            im = new ImageOverlay( cx._mapNode, image.get() );
            im->setCorners( 
                osg::Vec2d( p[0].x(), p[0].y() ),
                osg::Vec2d( p[1].x(), p[1].y() ),
                osg::Vec2d( p[3].x(), p[3].y() ),
                osg::Vec2d( p[2].x(), p[2].y() ) );
            cx._groupStack.top()->addChild( im );
        }
    }

    else {
        OE_WARN << LC << "GroundOverlay missing required LatLonBox/gx:LatLonQuad element" << std::endl;
        return;
    }


    // superclass build always called last
    KML_Overlay::build( conf, cx, im );
}
int
main(int argc, char** argv)
{
    osg::Group* root = new osg::Group();

    // try to load an earth file.
    osg::ArgumentParser arguments(&argc,argv);

    osgViewer::Viewer viewer(arguments);
    viewer.setCameraManipulator( new EarthManipulator() );

    // load an earth file and parse demo arguments
    osg::Node* node = MapNodeHelper().load(arguments, &viewer);
    if ( !node )
        return usage(argv);

    root->addChild( node );

    // find the map node that we loaded.
    MapNode* mapNode = MapNode::findMapNode(node);
    if ( !mapNode )
        return usage(argv);

    // Group to hold all our annotation elements.
    osg::Group* annoGroup = new osg::Group();
    root->addChild( annoGroup );

    //A group for all the editors
    osg::Group* editorGroup = new osg::Group;
    root->addChild( editorGroup );
    editorGroup->setNodeMask( 0 );

    HBox* box = ControlCanvas::getOrCreate(&viewer)->addControl( new HBox() );
    box->setChildSpacing( 5 );
    //Add a toggle button to toggle editing
    CheckBoxControl* editCheckbox = new CheckBoxControl( false );
    editCheckbox->addEventHandler( new ToggleNodeHandler( editorGroup ) );
    box->addControl( editCheckbox );
    LabelControl* labelControl = new LabelControl( "Edit Annotations" );
    labelControl->setFontSize( 24.0f );
    box->addControl( labelControl  );


    // Make a group for 2D items, and activate the decluttering engine. Decluttering
    // will migitate overlap between elements that occupy the same screen real estate.
    osg::Group* labelGroup = new osg::Group();
    Decluttering::setEnabled( labelGroup->getOrCreateStateSet(), true );
    annoGroup->addChild( labelGroup );

    // Style our labels:
    Style labelStyle;
    labelStyle.getOrCreate<TextSymbol>()->alignment() = TextSymbol::ALIGN_CENTER_CENTER;
    labelStyle.getOrCreate<TextSymbol>()->fill()->color() = Color::Yellow;

    // A lat/long SRS for specifying points.
    const SpatialReference* geoSRS = mapNode->getMapSRS()->getGeographicSRS();

    //--------------------------------------------------------------------

    // A series of place nodes (an icon with a text label)
    {
        Style pin;
        pin.getOrCreate<IconSymbol>()->url()->setLiteral( "../data/placemark32.png" );

        // bunch of pins:
        labelGroup->addChild( new PlaceNode(mapNode, GeoPoint(geoSRS, -74.00, 40.71), "New York"      , pin));
        labelGroup->addChild( new PlaceNode(mapNode, GeoPoint(geoSRS, -77.04, 38.85), "Washington, DC", pin));
        labelGroup->addChild( new PlaceNode(mapNode, GeoPoint(geoSRS,-118.40, 33.93), "Los Angeles"   , pin));
        labelGroup->addChild( new PlaceNode(mapNode, GeoPoint(geoSRS, -71.03, 42.37), "Boston"        , pin));
        labelGroup->addChild( new PlaceNode(mapNode, GeoPoint(geoSRS,-157.93, 21.35), "Honolulu"      , pin));
        labelGroup->addChild( new PlaceNode(mapNode, GeoPoint(geoSRS, 139.75, 35.68), "Tokyo"         , pin));
        labelGroup->addChild( new PlaceNode(mapNode, GeoPoint(geoSRS, -90.25, 29.98), "New Orleans"   , pin));
        labelGroup->addChild( new PlaceNode(mapNode, GeoPoint(geoSRS, -80.28, 25.82), "Miami"         , pin));
        labelGroup->addChild( new PlaceNode(mapNode, GeoPoint(geoSRS,-117.17, 32.72), "San Diego"     , pin));

        // test with an LOD:
        osg::LOD* lod = new osg::LOD();
        lod->addChild( new PlaceNode(mapNode, GeoPoint(geoSRS, 14.68, 50.0), "Prague", pin), 0.0, 1e6);
        labelGroup->addChild( lod );

        // absolute altitude:
        labelGroup->addChild( new PlaceNode(mapNode, GeoPoint(geoSRS, -87.65, 41.90, 1000, ALTMODE_ABSOLUTE), "Chicago"       , pin));
    }

    //--------------------------------------------------------------------

    // a box that follows lines of latitude (rhumb line interpolation, the default)
    {
        Geometry* geom = new Polygon();
        geom->push_back( osg::Vec3d(0,   40, 0) );
        geom->push_back( osg::Vec3d(-60, 40, 0) );
        geom->push_back( osg::Vec3d(-60, 60, 0) );
        geom->push_back( osg::Vec3d(0,   60, 0) );
        Style geomStyle;
        geomStyle.getOrCreate<LineSymbol>()->stroke()->color() = Color::Cyan;
        geomStyle.getOrCreate<LineSymbol>()->stroke()->width() = 5.0f;
        geomStyle.getOrCreate<AltitudeSymbol>()->clamping() = AltitudeSymbol::CLAMP_TO_TERRAIN;
        geomStyle.getOrCreate<AltitudeSymbol>()->technique() = AltitudeSymbol::TECHNIQUE_GPU;
        FeatureNode* gnode = new FeatureNode(mapNode, new Feature(geom, geoSRS, geomStyle));
        annoGroup->addChild( gnode );

        labelGroup->addChild( new LabelNode(mapNode, GeoPoint(geoSRS,-30, 50), "Rhumb line polygon", labelStyle) );
    }

    //--------------------------------------------------------------------

    // another rhumb box that crosses the antimeridian
    {
        Geometry* geom = new Polygon();
        geom->push_back( -160., -30. );
        geom->push_back(  150., -20. );
        geom->push_back(  160., -45. );
        geom->push_back( -150., -40. );
        Style geomStyle;
        geomStyle.getOrCreate<LineSymbol>()->stroke()->color() = Color::Lime;
        geomStyle.getOrCreate<LineSymbol>()->stroke()->width() = 3.0f;
        geomStyle.getOrCreate<AltitudeSymbol>()->clamping() = AltitudeSymbol::CLAMP_TO_TERRAIN;
        geomStyle.getOrCreate<AltitudeSymbol>()->technique() = AltitudeSymbol::TECHNIQUE_GPU;
        FeatureNode* gnode = new FeatureNode(mapNode, new Feature(geom, geoSRS, geomStyle));
        annoGroup->addChild( gnode );

        labelGroup->addChild( new LabelNode(mapNode, GeoPoint(geoSRS, -175, -35), "Antimeridian polygon", labelStyle) );
    }

    //--------------------------------------------------------------------

    // A path using great-circle interpolation.
    {
        Geometry* path = new LineString();
        path->push_back( osg::Vec3d(-74, 40.714, 0) );   // New York
        path->push_back( osg::Vec3d(139.75, 35.68, 0) ); // Tokyo

        Style pathStyle;
        pathStyle.getOrCreate<LineSymbol>()->stroke()->color() = Color::Red;
        pathStyle.getOrCreate<LineSymbol>()->stroke()->width() = 3.0f;
        pathStyle.getOrCreate<AltitudeSymbol>()->clamping() = AltitudeSymbol::CLAMP_TO_TERRAIN;
        pathStyle.getOrCreate<AltitudeSymbol>()->technique() = AltitudeSymbol::TECHNIQUE_GPU;

        Feature* pathFeature = new Feature(path, geoSRS, pathStyle);
        pathFeature->geoInterp() = GEOINTERP_GREAT_CIRCLE;

        //OE_INFO << "Path extent = " << pathFeature->getExtent().toString() << std::endl;

        FeatureNode* pathNode = new FeatureNode(mapNode, pathFeature);
        annoGroup->addChild( pathNode );

        labelGroup->addChild( new LabelNode(mapNode, GeoPoint(geoSRS,-170, 61.2), "Great circle path", labelStyle) );
    }

    //--------------------------------------------------------------------

    // Two circle segments around New Orleans.
    {
        Style circleStyle;
        circleStyle.getOrCreate<PolygonSymbol>()->fill()->color() = Color(Color::Cyan, 0.5);
        circleStyle.getOrCreate<AltitudeSymbol>()->clamping() = AltitudeSymbol::CLAMP_TO_TERRAIN;
        circleStyle.getOrCreate<AltitudeSymbol>()->technique() = AltitudeSymbol::TECHNIQUE_DRAPE;

        CircleNode* circle = new CircleNode(
            mapNode,
            GeoPoint(geoSRS, -90.25, 29.98, 1000., ALTMODE_RELATIVE),
            Distance(300, Units::KILOMETERS),
            circleStyle, Angle(-45.0, Units::DEGREES), Angle(45.0, Units::DEGREES), true);
        annoGroup->addChild( circle );

        editorGroup->addChild( new CircleNodeEditor( circle ) );
    }

	{
		Style circleStyle;
		circleStyle.getOrCreate<PolygonSymbol>()->fill()->color() = Color(Color::Red, 0.5);
		circleStyle.getOrCreate<AltitudeSymbol>()->clamping() = AltitudeSymbol::CLAMP_TO_TERRAIN;
		circleStyle.getOrCreate<AltitudeSymbol>()->technique() = AltitudeSymbol::TECHNIQUE_DRAPE;

		CircleNode* circle = new CircleNode(
			mapNode,
			GeoPoint(geoSRS, -90.25, 29.98, 1000., ALTMODE_RELATIVE),
			Distance(300, Units::KILOMETERS),
			circleStyle, Angle(45.0, Units::DEGREES), Angle(360.0 - 45.0, Units::DEGREES), true);
		annoGroup->addChild( circle );

		editorGroup->addChild( new CircleNodeEditor( circle ) );
	}

    //--------------------------------------------------------------------

    // An extruded ellipse around Miami.
    {
        Style ellipseStyle;
        ellipseStyle.getOrCreate<PolygonSymbol>()->fill()->color() = Color(Color::Orange, 0.75);
        ellipseStyle.getOrCreate<ExtrusionSymbol>()->height() = 250000.0; // meters MSL
        EllipseNode* ellipse = new EllipseNode(
            mapNode, 
            GeoPoint(geoSRS, -80.28, 25.82, 0.0, ALTMODE_RELATIVE),
            Distance(250, Units::MILES),
            Distance(100, Units::MILES),
            Angle   (0, Units::DEGREES),
            ellipseStyle,
            Angle(45.0, Units::DEGREES),
            Angle(360.0 - 45.0, Units::DEGREES), 
            true);
        annoGroup->addChild( ellipse );
        editorGroup->addChild( new EllipseNodeEditor( ellipse ) );
    }
	{
		Style ellipseStyle;
		ellipseStyle.getOrCreate<PolygonSymbol>()->fill()->color() = Color(Color::Blue, 0.75);
		ellipseStyle.getOrCreate<ExtrusionSymbol>()->height() = 250000.0; // meters MSL
		EllipseNode* ellipse = new EllipseNode(
			mapNode, 
			GeoPoint(geoSRS, -80.28, 25.82, 0.0, ALTMODE_RELATIVE),
			Distance(250, Units::MILES),
			Distance(100, Units::MILES),
			Angle   (0, Units::DEGREES),
			ellipseStyle, 
            Angle(-40.0, Units::DEGREES), 
            Angle(40.0, Units::DEGREES), 
            true);
		annoGroup->addChild( ellipse );
		editorGroup->addChild( new EllipseNodeEditor( ellipse ) );
	}
    
    //--------------------------------------------------------------------

    {
        // A rectangle around San Diego
        Style rectStyle;
        rectStyle.getOrCreate<PolygonSymbol>()->fill()->color() = Color(Color::Green, 0.5);
        rectStyle.getOrCreate<AltitudeSymbol>()->clamping() = AltitudeSymbol::CLAMP_TO_TERRAIN;
        rectStyle.getOrCreate<AltitudeSymbol>()->technique() = AltitudeSymbol::TECHNIQUE_DRAPE;
        RectangleNode* rect = new RectangleNode(
            mapNode, 
            GeoPoint(geoSRS, -117.172, 32.721),
            Distance(300, Units::KILOMETERS ),
            Distance(600, Units::KILOMETERS ),
            rectStyle);
        annoGroup->addChild( rect );

        editorGroup->addChild( new RectangleNodeEditor( rect ) );
    }    

    //--------------------------------------------------------------------

    // An extruded polygon roughly the shape of Utah. Here we demonstrate the
    // FeatureNode, where you create a geographic geometry and use it as an
    // annotation.
    {
        Geometry* utah = new Polygon();
        utah->push_back( -114.052, 37.0   );
        utah->push_back( -109.054, 37.0   );
        utah->push_back( -109.054, 41.0   );
        utah->push_back( -111.040, 41.0   );
        utah->push_back( -111.080, 42.059 );
        utah->push_back( -114.080, 42.024 );

        Style utahStyle;
        utahStyle.getOrCreate<ExtrusionSymbol>()->height() = 250000.0; // meters MSL
        utahStyle.getOrCreate<PolygonSymbol>()->fill()->color() = Color(Color::White, 0.8);

        Feature*     utahFeature = new Feature(utah, geoSRS, utahStyle);
        FeatureNode* featureNode = new FeatureNode(mapNode, utahFeature);
        annoGroup->addChild( featureNode );
    }

    //--------------------------------------------------------------------

    // an image overlay.
    {
        ImageOverlay* imageOverlay = 0L;
        osg::Image* image = osgDB::readImageFile( "../data/USFLAG.TGA" );
        if ( image )
        {
            imageOverlay = new ImageOverlay(mapNode, image);
            imageOverlay->setBounds( Bounds( -100.0, 35.0, -90.0, 40.0) );
            annoGroup->addChild( imageOverlay );

            editorGroup->addChild( new ImageOverlayEditor( imageOverlay ) );
        }
    }
    
    //--------------------------------------------------------------------

    // install decoration. These change the appearance of an Annotation
    // based on some user action.

    // highlight annotation upon hover by default:
    
    DecorationInstaller highlightInstaller("hover", new HighlightDecoration());
    annoGroup->accept( highlightInstaller );

    // scale labels when hovering:
    DecorationInstaller scaleInstaller("hover", new ScaleDecoration(1.1f));
    labelGroup->accept( scaleInstaller );

    // install an event handler for picking and hovering.
    AnnotationEventCallback* cb = new AnnotationEventCallback();
    cb->addHandler( new MyAnnoEventHandler() );

    annoGroup->addEventCallback( cb );

    //--------------------------------------------------------------------

    // initialize the viewer:    
    viewer.setSceneData( root );

    viewer.getCamera()->addCullCallback( new AutoClipPlaneCullCallback(mapNode) );
    viewer.addEventHandler(new osgViewer::StatsHandler());
    viewer.addEventHandler(new osgViewer::WindowSizeHandler());
    viewer.addEventHandler(new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()));

    return viewer.run();
}
Exemple #11
-1
int
main(int argc, char** argv)
{
    osg::Group* root = new osg::Group();

    // try to load an earth file.
    osg::ArgumentParser arguments(&argc,argv);

    osgViewer::Viewer viewer(arguments);
    viewer.setCameraManipulator( new EarthManipulator() );

    // load an earth file and parse demo arguments
    osg::Node* node = MapNodeHelper().load(arguments, &viewer);
    if ( !node )
        return usage(argv);

    root->addChild( node );

    // find the map node that we loaded.
    MapNode* mapNode = MapNode::findMapNode(node);
    if ( !mapNode )
        return usage(argv);

    // Group to hold all our annotation elements.
    osg::Group* annoGroup = new osg::Group();
    root->addChild( annoGroup );

    // Make a group for labels
    osg::Group* labelGroup = new osg::Group();
    annoGroup->addChild( labelGroup );

    osg::Group* editGroup = new osg::Group();
    root->addChild( editGroup );

    // Style our labels:
    Style labelStyle;
    labelStyle.getOrCreate<TextSymbol>()->alignment() = TextSymbol::ALIGN_CENTER_CENTER;
    labelStyle.getOrCreate<TextSymbol>()->fill()->color() = Color::Yellow;

    // A lat/long SRS for specifying points.
    const SpatialReference* geoSRS = mapNode->getMapSRS()->getGeographicSRS();

    //--------------------------------------------------------------------

    // A series of place nodes (an icon with a text label)
    {
        Style pm;
        pm.getOrCreate<IconSymbol>()->url()->setLiteral( "../data/placemark32.png" );
        pm.getOrCreate<IconSymbol>()->declutter() = true;
        pm.getOrCreate<TextSymbol>()->halo() = Color("#5f5f5f");

        // bunch of pins:
        labelGroup->addChild( new PlaceNode(mapNode, GeoPoint(geoSRS, -74.00, 40.71), "New York"      , pm));
        labelGroup->addChild( new PlaceNode(mapNode, GeoPoint(geoSRS, -77.04, 38.85), "Washington, DC", pm));
        labelGroup->addChild( new PlaceNode(mapNode, GeoPoint(geoSRS,-118.40, 33.93), "Los Angeles"   , pm));
        labelGroup->addChild( new PlaceNode(mapNode, GeoPoint(geoSRS, -71.03, 42.37), "Boston"        , pm));
        labelGroup->addChild( new PlaceNode(mapNode, GeoPoint(geoSRS,-157.93, 21.35), "Honolulu"      , pm));
        labelGroup->addChild( new PlaceNode(mapNode, GeoPoint(geoSRS, 139.75, 35.68), "Tokyo"         , pm));
        labelGroup->addChild( new PlaceNode(mapNode, GeoPoint(geoSRS, -90.25, 29.98), "New Orleans"   , pm));
        labelGroup->addChild( new PlaceNode(mapNode, GeoPoint(geoSRS, -80.28, 25.82), "Miami"         , pm));
        labelGroup->addChild( new PlaceNode(mapNode, GeoPoint(geoSRS,-117.17, 32.72), "San Diego"     , pm));

        // test with an LOD:
        osg::LOD* lod = new osg::LOD();
        lod->addChild( new PlaceNode(mapNode, GeoPoint(geoSRS, 14.68, 50.0), "Prague", pm), 0.0, 2e6);
        labelGroup->addChild( lod );

        // absolute altitude:
        labelGroup->addChild( new PlaceNode(mapNode, GeoPoint(geoSRS, -87.65, 41.90, 1000, ALTMODE_ABSOLUTE), "Chicago", pm));
    }

    //--------------------------------------------------------------------

    // a box that follows lines of latitude (rhumb line interpolation, the default)
    {
        Geometry* geom = new Polygon();
        geom->push_back( osg::Vec3d(0,   40, 0) );
        geom->push_back( osg::Vec3d(-60, 40, 0) );
        geom->push_back( osg::Vec3d(-60, 60, 0) );
        geom->push_back( osg::Vec3d(0,   60, 0) );

        Feature* feature = new Feature(geom, geoSRS);
        feature->geoInterp() = GEOINTERP_RHUMB_LINE;

        Style geomStyle;
        geomStyle.getOrCreate<LineSymbol>()->stroke()->color() = Color::Cyan;
        geomStyle.getOrCreate<LineSymbol>()->stroke()->width() = 5.0f;
        geomStyle.getOrCreate<LineSymbol>()->tessellationSize() = 75000;
        geomStyle.getOrCreate<AltitudeSymbol>()->clamping() = AltitudeSymbol::CLAMP_TO_TERRAIN;
        geomStyle.getOrCreate<AltitudeSymbol>()->technique() = AltitudeSymbol::TECHNIQUE_GPU;
        
        FeatureNode* fnode = new FeatureNode(mapNode, feature, geomStyle);
        
        annoGroup->addChild( fnode );

        labelGroup->addChild( new LabelNode(mapNode, GeoPoint(geoSRS,-30, 50), "Rhumb line polygon", labelStyle) );
    }

    //--------------------------------------------------------------------

    // another rhumb box that crosses the antimeridian
    {
        Geometry* geom = new Polygon();
        geom->push_back( -160., -30. );
        geom->push_back(  150., -20. );
        geom->push_back(  160., -45. );
        geom->push_back( -150., -40. );
        Style geomStyle;

        Feature* feature = new Feature(geom, geoSRS);
        feature->geoInterp() = GEOINTERP_RHUMB_LINE;

        geomStyle.getOrCreate<LineSymbol>()->stroke()->color() = Color::Lime;
        geomStyle.getOrCreate<LineSymbol>()->stroke()->width() = 3.0f;
        geomStyle.getOrCreate<LineSymbol>()->tessellationSize() = 75000;
        geomStyle.getOrCreate<AltitudeSymbol>()->clamping() = AltitudeSymbol::CLAMP_TO_TERRAIN;
        geomStyle.getOrCreate<AltitudeSymbol>()->technique() = AltitudeSymbol::TECHNIQUE_GPU;

        FeatureNode* gnode = new FeatureNode(mapNode, feature, geomStyle);
        annoGroup->addChild( gnode );

        labelGroup->addChild( new LabelNode(mapNode, GeoPoint(geoSRS, -175, -35), "Antimeridian polygon", labelStyle) );
    }

    //--------------------------------------------------------------------



    // A path using great-circle interpolation.
    // Keep a pointer to it so we can modify it later on.
    FeatureNode* pathNode = 0;
    {
        Geometry* path = new LineString();
        path->push_back( osg::Vec3d(-74, 40.714, 0) );   // New York
        path->push_back( osg::Vec3d(139.75, 35.68, 0) ); // Tokyo

        Feature* pathFeature = new Feature(path, geoSRS);
        pathFeature->geoInterp() = GEOINTERP_GREAT_CIRCLE;

        Style pathStyle;
        pathStyle.getOrCreate<LineSymbol>()->stroke()->color() = Color::White;
        pathStyle.getOrCreate<LineSymbol>()->stroke()->width() = 1.0f;
        pathStyle.getOrCreate<LineSymbol>()->tessellationSize() = 75000;
        pathStyle.getOrCreate<PointSymbol>()->size() = 5;
        pathStyle.getOrCreate<PointSymbol>()->fill()->color() = Color::Red;
        pathStyle.getOrCreate<AltitudeSymbol>()->clamping() = AltitudeSymbol::CLAMP_TO_TERRAIN;
        pathStyle.getOrCreate<AltitudeSymbol>()->technique() = AltitudeSymbol::TECHNIQUE_GPU;

        //OE_INFO << "Path extent = " << pathFeature->getExtent().toString() << std::endl;

        pathNode = new FeatureNode(mapNode, pathFeature, pathStyle);
        annoGroup->addChild( pathNode );

        labelGroup->addChild( new LabelNode(mapNode, GeoPoint(geoSRS,-170, 61.2), "Great circle path", labelStyle) );
    }

    //--------------------------------------------------------------------

    // Two circle segments around New Orleans.
    {
        Style circleStyle;
        circleStyle.getOrCreate<PolygonSymbol>()->fill()->color() = Color(Color::Cyan, 0.5);
        circleStyle.getOrCreate<AltitudeSymbol>()->clamping() = AltitudeSymbol::CLAMP_TO_TERRAIN;
        circleStyle.getOrCreate<AltitudeSymbol>()->technique() = AltitudeSymbol::TECHNIQUE_DRAPE;

        CircleNode* circle = new CircleNode(
            mapNode,
            GeoPoint(geoSRS, -90.25, 29.98, 1000., ALTMODE_RELATIVE),
            Distance(300, Units::KILOMETERS),
            circleStyle, Angle(-45.0, Units::DEGREES), Angle(45.0, Units::DEGREES), true);
        annoGroup->addChild( circle );

        editGroup->addChild( new CircleNodeEditor(circle) );
    }

	{
		Style circleStyle;
		circleStyle.getOrCreate<PolygonSymbol>()->fill()->color() = Color(Color::Red, 0.5);
		circleStyle.getOrCreate<AltitudeSymbol>()->clamping() = AltitudeSymbol::CLAMP_TO_TERRAIN;
		circleStyle.getOrCreate<AltitudeSymbol>()->technique() = AltitudeSymbol::TECHNIQUE_DRAPE;

		CircleNode* circle = new CircleNode(
			mapNode,
			GeoPoint(geoSRS, -90.25, 29.98, 1000., ALTMODE_RELATIVE),
			Distance(300, Units::KILOMETERS),
			circleStyle, Angle(45.0, Units::DEGREES), Angle(360.0 - 45.0, Units::DEGREES), true);
		annoGroup->addChild( circle );

        editGroup->addChild( new CircleNodeEditor(circle) );
	}

    //--------------------------------------------------------------------

    // An extruded ellipse around Miami.
    {
        Style ellipseStyle;
        ellipseStyle.getOrCreate<PolygonSymbol>()->fill()->color() = Color(Color::Orange, 0.75);
        ellipseStyle.getOrCreate<ExtrusionSymbol>()->height() = 250000.0; // meters MSL
        EllipseNode* ellipse = new EllipseNode(
            mapNode, 
            GeoPoint(geoSRS, -80.28, 25.82, 0.0, ALTMODE_RELATIVE),
            Distance(250, Units::MILES),
            Distance(100, Units::MILES),
            Angle   (0, Units::DEGREES),
            ellipseStyle,
            Angle(45.0, Units::DEGREES),
            Angle(360.0 - 45.0, Units::DEGREES), 
            true);
        annoGroup->addChild( ellipse );

        editGroup->addChild( new EllipseNodeEditor(ellipse) );
    }
	{
		Style ellipseStyle;
		ellipseStyle.getOrCreate<PolygonSymbol>()->fill()->color() = Color(Color::Blue, 0.75);
		ellipseStyle.getOrCreate<ExtrusionSymbol>()->height() = 250000.0; // meters MSL
		EllipseNode* ellipse = new EllipseNode(
			mapNode, 
			GeoPoint(geoSRS, -80.28, 25.82, 0.0, ALTMODE_RELATIVE),
			Distance(250, Units::MILES),
			Distance(100, Units::MILES),
			Angle   (0, Units::DEGREES),
			ellipseStyle, 
            Angle(-40.0, Units::DEGREES), 
            Angle(40.0, Units::DEGREES), 
            true);
		annoGroup->addChild( ellipse );

        editGroup->addChild( new EllipseNodeEditor(ellipse) );
	}
    
    //--------------------------------------------------------------------

    {
        // A rectangle around San Diego
        Style rectStyle;
        rectStyle.getOrCreate<PolygonSymbol>()->fill()->color() = Color(Color::Green, 0.5);
        rectStyle.getOrCreate<AltitudeSymbol>()->clamping() = AltitudeSymbol::CLAMP_TO_TERRAIN;
        rectStyle.getOrCreate<AltitudeSymbol>()->technique() = AltitudeSymbol::TECHNIQUE_DRAPE;
        RectangleNode* rect = new RectangleNode(
            mapNode, 
            GeoPoint(geoSRS, -117.172, 32.721),
            Distance(300, Units::KILOMETERS ),
            Distance(600, Units::KILOMETERS ),
            rectStyle);
        annoGroup->addChild( rect );

        editGroup->addChild( new RectangleNodeEditor(rect) );
    }    

    //--------------------------------------------------------------------

    // An extruded polygon roughly the shape of Utah. Here we demonstrate the
    // FeatureNode, where you create a geographic geometry and use it as an
    // annotation.
    {
        Geometry* utah = new Polygon();
        utah->push_back( -114.052, 37.0   );
        utah->push_back( -109.054, 37.0   );
        utah->push_back( -109.054, 41.0   );
        utah->push_back( -111.040, 41.0   );
        utah->push_back( -111.080, 42.059 );
        utah->push_back( -114.080, 42.024 );

        Style utahStyle;
        utahStyle.getOrCreate<ExtrusionSymbol>()->height() = 250000.0; // meters MSL
        utahStyle.getOrCreate<PolygonSymbol>()->fill()->color() = Color(Color::White, 0.8);

        Feature*     utahFeature = new Feature(utah, geoSRS);
        FeatureNode* featureNode = new FeatureNode(mapNode, utahFeature, utahStyle);
        annoGroup->addChild( featureNode );
    }

    //--------------------------------------------------------------------

    // an image overlay.
    {
        ImageOverlay* imageOverlay = 0L;
        osg::Image* image = osgDB::readImageFile( "../data/USFLAG.TGA" );
        if ( image )
        {
            imageOverlay = new ImageOverlay(mapNode, image);
            imageOverlay->setBounds( Bounds( -100.0, 35.0, -90.0, 40.0) );
            annoGroup->addChild( imageOverlay );

            editGroup->addChild( new ImageOverlayEditor(imageOverlay) );
        }
    }

    //--------------------------------------------------------------------

    // initialize the viewer:    
    viewer.setSceneData( root );    
    viewer.getCamera()->setSmallFeatureCullingPixelSize(-1.0f);
    return viewer.run();
}