osgEarth基础入门

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即可得到下面的效果。

  1. //引入osg和osgEarth的头文件和命名空间
  2. #include <osgGA/StateSetManipulator>
  3. #include <osgGA/GUIEventHandler>
  4. #include <osgViewer/Viewer>
  5. #include <osgViewer/ViewerEventHandlers>
  6. #include <osgUtil/LineSegmentIntersector>
  7. #include <osgEarth/MapNode>
  8. #include <osgEarth/TerrainEngineNode>
  9. #include <osgEarth/ElevationQuery>
  10. #include <osgEarth/StringUtils>
  11. #include <osgEarth/Terrain>
  12. #include <osgEarthUtil/EarthManipulator>
  13. #include <osgEarthUtil/Controls>
  14. #include <osgEarthUtil/LatLongFormatter>
  15. #include <iomanip>
  16.  
  17. using namespace osgEarth;
  18. using namespace osgEarth::Util;
  19. using namespace osgEarth::Util::Controls;
  20.  
  21. static MapNode* s_mapNode = 0L;
  22. static LabelControl* s_posLabel = 0L;
  23. static LabelControl* s_vdaLabel = 0L;
  24. static LabelControl* s_mslLabel = 0L;
  25. static LabelControl* s_haeLabel = 0L;
  26. static LabelControl* s_mapLabel = 0L;
  27. static LabelControl* s_resLabel = 0L;
  28.  
  29. // An event handler that will print out the elevation at the clicked point
  30. //查询高程的一个事件回调,在场景有事件更新触发时调用,详细参考osg或者osgGA::GUIEventHandler
  31. struct QueryElevationHandler : public osgGA::GUIEventHandler
  32. {
  33. //构造函数
  34. QueryElevationHandler()
  35. : _mouseDown( false ),
  36. _terrain ( s_mapNode->getTerrain() ),
  37. _query ( s_mapNode->getMap() )
  38. {
  39. _map = s_mapNode->getMap();
  40.  
  41. //初始化最大查询LOD级别
  42. _query.setMaxTilesToCache();
  43.  
  44. _path.push_back( s_mapNode->getTerrainEngine() );
  45. }
  46. //更新回调,具体的内容可以参考父类,传进来的参数是屏幕坐标xy,和osgViewer
  47. void update( float x, float y, osgViewer::View* view )
  48. {
  49. bool yes = false;
  50.  
  51. // look under the mouse:
  52. //采用线去对地球做碰撞检测,根据鼠标点击点去检测,得到交点,就是当前点的xyz
  53. osg::Vec3d world;
  54. osgUtil::LineSegmentIntersector::Intersections hits;
  55. //判断求交结果是否为空
  56. if ( view->computeIntersections(x, y, hits) )
  57. {
  58. //得到世界坐标系下面的坐标,就是osg的xyz坐标
  59. world = hits.begin()->getWorldIntersectPoint();
  60.  
  61. // convert to map coords:
  62. //将其转换为地球的地理坐标,转换方法都照抄即可
  63. GeoPoint mapPoint;
  64. mapPoint.fromWorld( _terrain->getSRS(), world );
  65.  
  66. // do an elevation query:
  67. ; // 1/10th of a degree
  68. double out_hamsl = 0.0;
  69. double out_resolution = 0.0;
  70.  
  71. //根据输入参数查询当前点位置的高程,需要设置分辨率,就是查询精度
  72. bool ok = _query.getElevation(
  73. mapPoint,
  74. out_hamsl,
  75. query_resolution,
  76. &out_resolution );
  77.  
  78. //如果查询成功
  79. if ( ok )
  80. {
  81. // convert to geodetic to get the HAE:
  82. mapPoint.z() = out_hamsl;
  83. GeoPoint mapPointGeodetic( s_mapNode->getMapSRS()->getGeodeticSRS(), mapPoint );
  84.  
  85. //经纬度坐标的格式化工具,也可以自己用字符串去拼接xyz数字
  86. static LatLongFormatter s_f;
  87.  
  88. //更新显示的xyz值,label是传入的控件
  89. s_posLabel->setText( Stringify()
  90. << std::)
  91. << s_f.format(mapPointGeodetic.y())
  92. << ", "
  93. << s_f.format(mapPointGeodetic.x()) );
  94.  
  95. //还可以输出分辨率,椭球体信息等
  96. s_mslLabel->setText( Stringify() << out_hamsl );
  97. s_haeLabel->setText( Stringify() << mapPointGeodetic.z() );
  98. s_resLabel->setText( Stringify() << out_resolution );
  99.  
  100. yes = true;
  101. }
  102.  
  103. // finally, get a normal ISECT HAE point.
  104. GeoPoint isectPoint;
  105. isectPoint.fromWorld( _terrain->getSRS()->getGeodeticSRS(), world );
  106. s_mapLabel->setText( Stringify() << isectPoint.alt() );
  107. }
  108.  
  109. //如果查询不到高程的话
  110. if (!yes)
  111. {
  112. s_posLabel->setText( "-" );
  113. s_mslLabel->setText( "-" );
  114. s_haeLabel->setText( "-" );
  115. s_resLabel->setText( "-" );
  116. }
  117. }
  118.  
  119. //参数一个是事件的动作,另外一个是对应的操作
  120. bool handle( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa )
  121. {
  122. //判断如果是移动鼠标事件才进行更新当前的坐标显示
  123. if (ea.getEventType() == osgGA::GUIEventAdapter::MOVE &&
  124. aa.asView()->getFrameStamp()->getFrameNumber() % == )
  125. {
  126. osgViewer::View* view = static_cast<osgViewer::View*>(aa.asView());
  127. update( ea.getX(), ea.getY(), view );
  128. }
  129.  
  130. return false;
  131. }
  132.  
  133. //Map对象
  134. const Map* _map;
  135. //地形对象
  136. const Terrain* _terrain;
  137. bool _mouseDown;
  138. //查询高程使用的对象
  139. ElevationQuery _query;
  140. osg::NodePath _path;
  141. };
  142.  
  143. //main函数,
  144. int main(int argc, char** argv)
  145. {
  146. //这儿两个参数,第一个是命令参数的个数为,后面是字符串数组输入earth文件的路径osg::ArgumentParser arguments(&argc,argv);
  147.  
  148. //osg的场景
  149. osgViewer::Viewer viewer(arguments);
  150. //构造MapNode,arguments里面有earth文件的路径,命令行输入
  151. s_mapNode = MapNode::load(arguments);
  152. //如果路径不正确或者earth文件错误,没有构造好MapNode
  153. if ( !s_mapNode )
  154. {
  155. OE_WARN << "Unable to load earth file." << std::endl;
  156. ;
  157. }
  158. //建立一个组节点
  159. osg::Group* root = new osg::Group();
  160. //将组节点设置为场景节点
  161. viewer.setSceneData( root );
  162.  
  163. // install the programmable manipulator.
  164. //设置earth操作器
  165. viewer.setCameraManipulator( new osgEarth::Util::EarthManipulator() );
  166.  
  167. // The MapNode will render the Map object in the scene graph.
  168. //将MapNode添加到组节点中去
  169. root->addChild( s_mapNode );
  170.  
  171. //下面是设置一个控件,grid的意思是用格网去布局里面的小控件
  172. // Make the readout:
  173. Grid* grid = new Grid();
  174. //设置几个Label文字控件显示在场景中的第行
  175. grid->setControl(,,new LabelControl("Coords (Lat, Long):"));
  176. grid->setControl(,,new LabelControl("Vertical Datum:"));
  177. grid->setControl(,,new LabelControl("Height (MSL):"));
  178. grid->setControl(,,new LabelControl("Height (HAE):"));
  179. grid->setControl(,,new LabelControl("Isect (HAE):"));
  180. grid->setControl(,,new LabelControl("Resolution:"));
  181. //设置几个Label文字控件显示在场景中的第行
  182. s_posLabel = grid->setControl(,,new LabelControl(""));
  183. s_vdaLabel = grid->setControl(,,new LabelControl(""));
  184. s_mslLabel = grid->setControl(,,new LabelControl(""));
  185. s_haeLabel = grid->setControl(,,new LabelControl(""));
  186. s_mapLabel = grid->setControl(,,new LabelControl(""));
  187. s_resLabel = grid->setControl(,,new LabelControl(""));
  188.  
  189. //得到空间参考,椭球面信息,并显示对应上面的label
  190. const SpatialReference* mapSRS = s_mapNode->getMapSRS();
  191. s_vdaLabel->setText( mapSRS->getVerticalDatum() ?
  192. mapSRS->getVerticalDatum()->getName() :
  193. Stringify() << "geodetic (" << mapSRS->getEllipsoid()->getName() << ")" );
  194.  
  195. //控件绘制容器
  196. ControlCanvas* canvas = new ControlCanvas();
  197. //将要显示的控件加入到root组节点中去
  198. root->addChild(canvas);
  199. canvas->addControl( grid );
  200.  
  201. //添加刚刚自定义的查询高程的事件回调
  202. // An event handler that will respond to mouse clicks:
  203. viewer.addEventHandler( new QueryElevationHandler() );
  204. //添加状态显示,窗口改变等事件回调
  205. // add some stock OSG handlers:
  206. viewer.addEventHandler(new osgViewer::StatsHandler());
  207. viewer.addEventHandler(new osgViewer::WindowSizeHandler());
  208. viewer.addEventHandler(new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()));
  209.  
  210. //run
  211. return viewer.run();
  212. }
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值