• osgEarth基础入门


    osgEarth基础入门

    2015年3月21日

    16:19

    osgEarth是基于三维引擎osg开发的三维数字地球引擎库,在osg基础上实现了瓦片调度插件,可选的四叉树调度插件,更多的地理数据加载插件(包括GDAL,ogr,WMS,TMS,VPB,filesystem等),再结合一套地理投影转换插件,这样就能够实现高效处理加载调度地理数据在三维地球上的显示,实现三维虚拟地球。

    想要实现一个简单的基于osgEarth的三维地球,有两种方式,这两种方式是互通的。一种基于XML标签的earth文件加载,另外一种是采用C++代码,本质是一样的,osgEarth内部支持直接解析XML标签文件,转换为代码实现,具体参考tests文件夹例子,代码则参考application下面例子。但是大多数情况下,你需要更灵活掌控性更强的代码来实现功能,因为只有一个加载了基础数据的三维地球是只能看,不能解决实际问题的,需要界面通常采用QT,更多的三维渲染和仿真业务则由osg来完成。因此学好osg是做这一切的基础。

    osgEarth的特点:支持加载常见的栅格数据(影像和DEM),但是大数据必须建立金字塔,设置为地理投影,想要高效率最好处理为瓦片,这样也便于部署在服务端。矢量数据,最好尽可能的简化,因为大的矢量会十分影响渲染速度,当然也可以对矢量栅格化处理加快速度,对于模型的话,大数据量一定要做LOD或者pageLod。

    osgEarth程序的常规流程:

    创建osgViewer---->创建MapNode---->设置Earth操作器---->设置场景参数----->run

    MapNode是继承自osg的Node,是osgEarth中地球节点,你所添加的影像,DEM,模型都包含在MapNode中,因为它们都加入到Map中,Map则类似二维中的Map可以添加各种图层。剩余的不管是模型节点Node,或者是标注Node,还是其他的都是可以直接添加到MapNode中或者另外的Group中。

    Earth操作器则和其他osg操作器一样,只不过专门为三维地球浏览定制,具体参数可以设置。

    场景参数则主要有自动地形裁剪,最小裁剪像素等其他优化场景的参数。

    下面就简单阐述一个小例子说明:

    代码功能主要实现了查询实时高程,并显示XYZ坐标的功能。

    使用命令app.exe test.earth即可得到下面的效果。

    clip_image002

    //引入osg和osgEarth的头文件和命名空间
    #include <osgGA/StateSetManipulator>
    #include <osgGA/GUIEventHandler>
    #include <osgViewer/Viewer>
    #include <osgViewer/ViewerEventHandlers>
    #include <osgUtil/LineSegmentIntersector>
    #include <osgEarth/MapNode>
    #include <osgEarth/TerrainEngineNode>
    #include <osgEarth/ElevationQuery>
    #include <osgEarth/StringUtils>
    #include <osgEarth/Terrain>
    #include <osgEarthUtil/EarthManipulator>
    #include <osgEarthUtil/Controls>
    #include <osgEarthUtil/LatLongFormatter>
    #include <iomanip>
     
    using namespace osgEarth;
    using namespace osgEarth::Util;
    using namespace osgEarth::Util::Controls;
     
    static MapNode*       s_mapNode     = 0L;
    static LabelControl*  s_posLabel    = 0L;
    static LabelControl*  s_vdaLabel    = 0L;
    static LabelControl*  s_mslLabel    = 0L;
    static LabelControl*  s_haeLabel    = 0L;
    static LabelControl*  s_mapLabel    = 0L;
    static LabelControl*  s_resLabel    = 0L;
     
     
    // An event handler that will print out the elevation at the clicked point
    //查询高程的一个事件回调,在场景有事件更新触发时调用,详细参考osg或者osgGA::GUIEventHandler 
    struct QueryElevationHandler : public osgGA::GUIEventHandler 
    {
    //构造函数
    QueryElevationHandler()
    : _mouseDown( false ),
    _terrain  ( s_mapNode->getTerrain() ),
    _query    ( s_mapNode->getMap() )
    {
    _map = s_mapNode->getMap();
     
    //初始化最大查询LOD级别
    _query.setMaxTilesToCache(10);
     
    _path.push_back( s_mapNode->getTerrainEngine() );
    }
    //更新回调,具体的内容可以参考父类,传进来的参数是屏幕坐标xy,和osgViewer
    void update( float x, float y, osgViewer::View* view )
    {
    bool yes = false;
     
    // look under the mouse:
    //采用线去对地球做碰撞检测,根据鼠标点击点去检测,得到交点,就是当前点的xyz
    osg::Vec3d world;
    osgUtil::LineSegmentIntersector::Intersections hits;
    //判断求交结果是否为空
    if ( view->computeIntersections(x, y, hits) )
    {
    //得到世界坐标系下面的坐标,就是osg的xyz坐标
    world = hits.begin()->getWorldIntersectPoint();
     
    // convert to map coords:
    //将其转换为地球的地理坐标,转换方法都照抄即可
    GeoPoint mapPoint;
    mapPoint.fromWorld( _terrain->getSRS(), world );
     
    // do an elevation query:
    double query_resolution = 0; // 1/10th of a degree
    double out_hamsl        = 0.0;
    double out_resolution   = 0.0;
     
    //根据输入参数查询当前点位置的高程,需要设置分辨率,就是查询精度
    bool ok = _query.getElevation( 
    mapPoint,
    out_hamsl,
    query_resolution, 
    &out_resolution );
     
    //如果查询成功
    if ( ok )
    {
    // convert to geodetic to get the HAE:
    mapPoint.z() = out_hamsl;
    GeoPoint mapPointGeodetic( s_mapNode->getMapSRS()->getGeodeticSRS(), mapPoint );
     
    //经纬度坐标的格式化工具,也可以自己用字符串去拼接xyz数字
    static LatLongFormatter s_f;
     
    //更新显示的xyz值,label是传入的控件
    s_posLabel->setText( Stringify()
    << std::fixed << std::setprecision(2) 
    << s_f.format(mapPointGeodetic.y())
    << ", " 
    << s_f.format(mapPointGeodetic.x()) );
     
    //还可以输出分辨率,椭球体信息等
    s_mslLabel->setText( Stringify() << out_hamsl );
    s_haeLabel->setText( Stringify() << mapPointGeodetic.z() );
    s_resLabel->setText( Stringify() << out_resolution );
     
    yes = true;
    }
     
    // finally, get a normal ISECT HAE point.
    GeoPoint isectPoint;
    isectPoint.fromWorld( _terrain->getSRS()->getGeodeticSRS(), world );
    s_mapLabel->setText( Stringify() << isectPoint.alt() );
    }
     
    //如果查询不到高程的话
    if (!yes)
    {
    s_posLabel->setText( "-" );
    s_mslLabel->setText( "-" );
    s_haeLabel->setText( "-" );
    s_resLabel->setText( "-" );
    }
    }
     
    //参数一个是事件的动作,另外一个是对应的操作
    bool handle( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa )
    {
    //判断如果是移动鼠标事件才进行更新当前的坐标显示
    if (ea.getEventType() == osgGA::GUIEventAdapter::MOVE &&
    aa.asView()->getFrameStamp()->getFrameNumber() % 10 == 0)
    {
    osgViewer::View* view = static_cast<osgViewer::View*>(aa.asView());
    update( ea.getX(), ea.getY(), view );
    }
     
    return false;
    }
     
    //Map对象
    const Map*       _map;
    //地形对象
    const Terrain*   _terrain;
    bool             _mouseDown;
    //查询高程使用的对象
    ElevationQuery   _query;
    osg::NodePath    _path;
    };
     
    //main函数,
    int main(int argc, char** argv)
    {
    //这儿两个参数,第一个是命令参数的个数为,后面是字符串数组输入earth文件的路径osg::ArgumentParser arguments(&argc,argv);
     
    //osg的场景
    osgViewer::Viewer viewer(arguments);
    //构造MapNode,arguments里面有earth文件的路径,命令行输入
    s_mapNode = MapNode::load(arguments);
    //如果路径不正确或者earth文件错误,没有构造好MapNode
    if ( !s_mapNode )
    {
    OE_WARN << "Unable to load earth file." << std::endl;
    return -1;
    }
    //建立一个组节点
    osg::Group* root = new osg::Group();
    //将组节点设置为场景节点
    viewer.setSceneData( root );
     
    // install the programmable manipulator.
    //设置earth操作器
    viewer.setCameraManipulator( new osgEarth::Util::EarthManipulator() );
     
    // The MapNode will render the Map object in the scene graph.
    //将MapNode添加到组节点中去
    root->addChild( s_mapNode );
     
    //下面是设置一个控件,grid的意思是用格网去布局里面的小控件
    // Make the readout:
    Grid* grid = new Grid();
    //设置几个Label文字控件显示在场景中的第行
    grid->setControl(0,0,new LabelControl("Coords (Lat, Long):"));
    grid->setControl(0,1,new LabelControl("Vertical Datum:"));
    grid->setControl(0,2,new LabelControl("Height (MSL):"));
    grid->setControl(0,3,new LabelControl("Height (HAE):"));
    grid->setControl(0,4,new LabelControl("Isect  (HAE):"));
    grid->setControl(0,5,new LabelControl("Resolution:"));
    //设置几个Label文字控件显示在场景中的第行
    s_posLabel = grid->setControl(1,0,new LabelControl(""));
    s_vdaLabel = grid->setControl(1,1,new LabelControl(""));
    s_mslLabel = grid->setControl(1,2,new LabelControl(""));
    s_haeLabel = grid->setControl(1,3,new LabelControl(""));
    s_mapLabel = grid->setControl(1,4,new LabelControl(""));
    s_resLabel = grid->setControl(1,5,new LabelControl(""));
     
    //得到空间参考,椭球面信息,并显示对应上面的label
    const SpatialReference* mapSRS = s_mapNode->getMapSRS();
    s_vdaLabel->setText( mapSRS->getVerticalDatum() ? 
    mapSRS->getVerticalDatum()->getName() : 
    Stringify() << "geodetic (" << mapSRS->getEllipsoid()->getName() << ")" );
     
    //控件绘制容器
    ControlCanvas* canvas = new ControlCanvas();    
    //将要显示的控件加入到root组节点中去
    root->addChild(canvas);
    canvas->addControl( grid );
     
    //添加刚刚自定义的查询高程的事件回调
    // An event handler that will respond to mouse clicks:
    viewer.addEventHandler( new QueryElevationHandler() );
    //添加状态显示,窗口改变等事件回调
    // add some stock OSG handlers:
    viewer.addEventHandler(new osgViewer::StatsHandler());
    viewer.addEventHandler(new osgViewer::WindowSizeHandler());
    viewer.addEventHandler(new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()));
     
    //run
    return viewer.run();
    }
  • 相关阅读:
    Linux内核同步方法
    C++11写轻量级AOP框架
    Typora夜樱主题
    MySQL添加主键和外键
    命题连接词和命题逻辑
    打印一个类全部信息的方法
    getClass()和instanceof以及类的equals方法
    多态
    在构造函数中调用另一个构造函数
    参数传递
  • 原文地址:https://www.cnblogs.com/sunliming/p/4355966.html
Copyright © 2020-2023  润新知