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即可得到下面的效果。
//引入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();
}
osgEarth基础入门的更多相关文章
- osgEarth基础入门(转载)
osgEarth基础入门 osgEarth是基于三维引擎osg开发的三维数字地球引擎库,在osg基础上实现了瓦片调度插件,可选的四叉树调度插件,更多的地理数据加载插件(包括GDAL,ogr,WMS,T ...
- js学习笔记:webpack基础入门(一)
之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...
- 「译」JUnit 5 系列:基础入门
原文地址:http://blog.codefx.org/libraries/junit-5-basics/ 原文日期:25, Feb, 2016 译文首发:Linesh 的博客:JUnit 5 系列: ...
- .NET正则表达式基础入门
这是我第一次写的博客,个人觉得十分不容易.以前看别人写的博客文字十分流畅,到自己来写却发现十分困难,还是感谢那些为技术而奉献自己力量的人吧. 本教程编写之前,博主阅读了<正则指引>这本入门 ...
- 从零3D基础入门XNA 4.0(2)——模型和BasicEffect
[题外话] 上一篇文章介绍了3D开发基础与XNA开发程序的整体结构,以及使用Model类的Draw方法将模型绘制到屏幕上.本文接着上一篇文章继续,介绍XNA中模型的结构.BasicEffect的使用以 ...
- 从零3D基础入门XNA 4.0(1)——3D开发基础
[题外话] 最近要做一个3D动画演示的程序,由于比较熟悉C#语言,再加上XNA对模型的支持比较好,故选择了XNA平台.不过从网上找到很多XNA的入门文章,发现大都需要一些3D基础,而我之前并没有接触过 ...
- Shell编程菜鸟基础入门笔记
Shell编程基础入门 1.shell格式:例 shell脚本开发习惯 1.指定解释器 #!/bin/bash 2.脚本开头加版权等信息如:#DATE:时间,#author(作者)#mail: ...
- [Spring框架]Spring AOP基础入门总结二:Spring基于AspectJ的AOP的开发.
前言: 在上一篇中: [Spring框架]Spring AOP基础入门总结一. 中 我们已经知道了一个Spring AOP程序是如何开发的, 在这里呢我们将基于AspectJ来进行AOP 的总结和学习 ...
- [Spring框架]Spring AOP基础入门总结一.
前言:前面已经有两篇文章讲了Spring IOC/DI 以及 使用xml和注解两种方法开发的案例, 下面就来梳理一下Spring的另一核心AOP. 一, 什么是AOP 在软件业,AOP为Aspect ...
随机推荐
- CSS关于元素垂直居中的问题
今天碰到了一个问题,给一个父容器和一个子元素,子元素不定高和不定宽,怎么让子元素居中在父容器中,比如下段代码 方法1: <div class="div1"> <d ...
- spider autohome (1)
Code: #!/usr/bin/python # -*- coding: UTF-8 -*- import re import urllib import time def getHtml(url) ...
- paip.mysql 全文索引查询空白解决
paip.mysql 全文索引查询空白解决 或者 Incorrect key file for table: \'%s\'. Try to repair it 作者Attilax 艾龙, ...
- atitit.客户端连接oracle数据库的方式总结
客户端连接oracle数据库的方式总结 目录 Java程序连接一般使用jar驱动连接..... 桌面GUI一般采取c语言驱动oci.dll 直接连接... 间接连接(需要配置tns及其envi var ...
- iOS开发-UIScrollView原理
UIScrollView在开发中是不可避免,关于UIScrollView都有自己一定的理解.滚动视图有两个需要理解的属性,frame和bounds,frame是定义了视图在窗口的大小和位置,bound ...
- java方法创建
一个方法public(作用域) void(void是不要返回值,String返回String类型,User(自定义的类型)返回User类型) test(方法名) (int a(参数)){ } stat ...
- ooj1057: M的整数倍DP
http://121.249.217.157/JudgeOnline/problem.php?id=1057 1057: M的整数倍 时间限制: 1 Sec 内存限制: 64 MB提交: 130 ...
- unity 读取excel表 生成asset资源文件
做unity 项目也有一段时间了,从unity项目开发和学习中也遇到了很多坑,并且也从中学习到了很多曾经未接触的领域.项目中的很多功能模块,从今天开始把自己的思路和代码奉上给学渣们作为一份学习的资料. ...
- 日常开发中常见的HTTP协议的状态码
301Moved Permanently请求的网页已永久移动到新位置.服务器返回此响应(对 GET 或 HEAD 请求的响应)时,会自动将申请人转到新位置.您应使用此代码告诉 Googlebot 某个 ...
- XMPP系列1:简介
通俗解释其实XMPP 是一种很类似于http协议的一种数据传输协议,它的过程就如同“解包装--〉包装”的过程,用户只需要明白它接收的类型,并理解它返回的类型,就可以很好的利用xmpp来进行数据通讯.系 ...