基于osgEarth的简单数字地球

TIFF文件

  • TIFF为图像格式,以tif为后缀,是一种位图格式。
  • 同BMP一样,包含文件头、信息头、数据区。
  • 地球纹理和高程图用到了TIFF。
  • 用Global Mapper打开高程图可导出等高线。

Shape文件

  • Shape文件是以shp为后缀的二进制文件。
  • 用arcview打开可以看到其信息为点线面的几何图形,还包括几何图形的坐标,同样也是一张数据表。

OSG文件

  • OSG文件是以osg后缀的文本文件。
  • 使用osgviewer *.osg可快速浏览osg文件定义的模型。
  • OSG文件内规定了模型的节点层次,并设置了模型的属性,给出了模型各个顶点的坐标。

earth文件

  • earth文件本质是XML文本文件,后缀为earth。

  • map:文件的最外层标签,可认为是一个OSG节点,在代码中是osgEarth::MapNode

    • type:给出使用的坐标,我设置为球心坐标geocentric,若平面投影为Projected
    • version:osgEarth的大版本,我用的2.10,标记为2
    • name:map的名称。
  • heightfield定义高程数据。

    • name:高程数据名称。
    • driver:驱动。
    • url:高程数据路径,可以是本地或网络,我在本地保存了一份30m的高程图,格式为.tif。
  • image定义纹理,可以理解为一个图层。

    • name:高程数据名称。
    • driver:驱动。
    • url:高程数据路径,可以是本地或网络。
  • cache数据量大,加载慢,因此需要设置缓存。

    • type:缓存类型,我设置filesystem,即本地文件系统。
    • path:缓存路径。
<map name="globe" type="geocentric" version="2">
  <!-- 定义全球影像-->
  <image name="globeImage" driver="gdal">
    <url>../data/globe/globe.tif</url>
  </image>
  <!-- 全球高程-->
  <heightfield name="globeHeightfiled" driver="gdal">
    <url>../data/heightfield/30m.tif</url>
  </heightfield>
  <image name="detail" driver="composite">
    <!-- 局部高清纹理 复合影像(消除接缝、没有image数量限制)-->
    <image name="zhejiangImage" driver="gdal">
      <url>../data/detail/zhejiang.tif</url>
    </image>
    <image name="deqingImage" driver="gdal">
      <url>../data/detail/deqing.tif</url>
    </image>
    <image name="plateauImage" driver="gdal">
      <url>../data/detail/plateau.tif</url>
    </image>
  </image>
 
  <!-- 国界线-->
  <!-- agglite 将矢量文件栅格化 -->
  <image name="countryBoundary" driver="agglite">
    <!--子标签读取shape(画形状、贴图) ogr将shape换成纹理叠加到图上 -->
    <features name="world" driver="ogr">
      <url>../data/shpFile/world.shp</url>
      <!-- 建立空间索引(加快加载速度) -->
      <build_spatial_index>true</build_spatial_index>
    </features>
    <!-- 栅格化的类型(点线面) -->
    <geomerty_type>line</geomerty_type>
    <!-- 以像素为单位 -->
    <relative_line_size>true</relative_line_size>
    <!-- css设置风格 -->
    <styles>
      <style type="text/css">
        world {
          stroke: #ffff00;
          stroke-opacity: 1.0;
          stroke-width: 2.0;
        }
      </style>
    </styles>
  </image>
  <!-- 省界线 -->
  <image name="provinceBoundary" driver="agglite">
    <features name="china" driver="ogr">
      <url>../data/shpFile/china.shp</url>
      <build_spatial_index>true</build_spatial_index>
    </features>
    <geomerty_type>line</geomerty_type>
    <relative_line_size>true</relative_line_size>
    <styles>
      <style type="text/css">
        china {
          stroke: #ffffff;
          stroke-opacity: 1.0;
          stroke-width: 1.5;
        }
      </style>
    </styles>
  </image>
  <!-- 等高线 -->
  <image name="contour" driver="agglite">
    <features name="contour" driver="ogr">
      <url>../data/shpFile/plateau.shp</url>
      <build_spatial_index>true</build_spatial_index>
    </features>
    <geomerty_type>line</geomerty_type>
    <relative_line_size>true</relative_line_size>
    <styles>
      <style type="text/css">
        china {
          stroke: #000000;
          stroke-opacity: 1.0;
          stroke-width: 2;
        }
      </style>
    </styles>
  </image>
  <!-- 文件缓存-->
  <options>
    <cache type="filesystem">
      <path>./FileCache</path>
    </cache>
  </options>
</map>

OSG基本设置

  • 图形显示设置

    • 将OSG图形嵌入MFC单文档程序,用Traits类设置OSG窗口属性。

      // 获取窗口的矩形区域
      ::GetWindowRect(hWnd, &rect);
      // 创建窗口特征类
      osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
      // 获取windata
      osg::ref_ptr<osg::Referenced> windata = new osgViewer::GraphicsWindowWin32::WindowData(hWnd);
      // 设置显示的各个属性
      traits->x = 0;
      traits->y = 0;
      traits->width = rect.right - rect.left;
      traits->height = rect.bottom - rect.top;
      // 双缓冲
      traits->doubleBuffer = true;
      // 共享的图形环境null
      traits->sharedContext = nullptr;
      // 继承窗口的像素格式
      traits->setInheritedWindowPixelFormat = true;
      traits->inheritedWindowData = windata;
      
    • 将traits作为参数可构造OSG的图形上下文,并进而设置摄像头、视口、投影。

      // 创建上下文
      osg::GraphicsContext* gc = osg::GraphicsContext::createGraphicsContext(traits);
      // 创建摄像头
      osg::ref_ptr<osg::Camera> camera = new osg::Camera;
      camera->setGraphicsContext(gc);
      // 设置视口
      camera->setViewport(new osg::Viewport(traits->x, traits->y, traits->width, traits->height));
      // 设置视角
      camera->setProjectionMatrixAsPerspective(30.0f, static_cast<double>(traits->width) / static_cast<double>(traits->height), 1.0, 1000.0);
      // 设置摄像头
      viewer->setCamera(camera);
      // 设置场景
      viewer->setSceneData(sceneRoot);
      // 激活窗口并关联线程
      viewer->realize();
      
  • osgEarth操作器

    • 将操作器的操作节点设置为mapNode,即地球。

      earthManipulator = new osgEarth::Util::EarthManipulator;
      if (mapNode.valid())
      {
          earthManipulator->setNode(mapNode);
      }
      
    • 设置监视器的相机操作器。

      viewer->setCameraManipulator(earthManipulator);
      
    • 设置视点变换的平滑过渡。

      earthManipulator->getSettings()->setArcViewpointTransitions(true);
      earthManipulator->setViewpoint(osgEarth::Viewpoint(nullptr, 112.44, 33.75, 404.06, -3.55, -84.59, 22935594.99), 5);
      

OSG节点树

  • 定义osg::ref_ptr<osg::Group> sceneRoot为根节点。

  • osgDB::readNodeFile读入.earth文件,作为一个节点,并将其向派生类转换为osgEarth特有的地球节点。mapNode包含坐标系信息、osgEarth定义的地图的指针等信息,通过mapNode可得到.earth文件中的信息,即省界线、国界线、高清纹理等图层。

    void COSGObject::initSceneGraph()
    {
    	// 创建根节点对象
    	sceneRoot = new osg::Group;
    	// 读取影像
    	osg::ref_ptr<osg::Node> mp = osgDB::readNodeFile("./earthfile/DigitalMap.earth");
    	// 添加子节点
    	sceneRoot->addChild(mp);
    	// 父类向子类转换
    	mapNode = dynamic_cast<osgEarth::MapNode*>(mp.get());
    }
    
  • 星空节点:SkyNode类生成,直接连向根节点。

    void COSGObject::initSky()
    {
    	// 初始化天空
    	osg::ref_ptr<osgEarth::Util::SkyNode> skyNode = osgEarth::Util::SkyNode::create(mapNode);
    	skyNode->setDateTime(osgEarth::DateTime(2021, 12, 11, 15));
    	skyNode->attach(viewer, 1);
    	skyNode->setStarsVisible(true);
    	skyNode->setSunVisible(true);
    	sceneRoot->addChild(skyNode);
    }
    
  • 国旗节点:读取国旗图像作为节点,直接连向根节点。

    void COSGObject::initNationFlag()
    {
          // 初始化国旗标识
          nationFlag = new osg::Group;
          sceneRoot->addChild(nationFlag);
          osg::Image* chinaIcon = osgDB::readImageFile("data/label/chinaYES.png");
          osg::ref_ptr<osgEarth::Annotation::PlaceNode> placeNode = new osgEarth::Annotation::PlaceNode;
          placeNode->setIconImage(chinaIcon);
          placeNode->setPosition(osgEarth::GeoPoint(mapNode->getMapSRS()->getGeographicSRS(), 110, 34));
          nationFlag->addChild(placeNode.get());
    }
    
  • 三维模型(cow.osg)

    void COSGObject::init3DCow()
    {
          // 得到世界坐标下的转换矩阵
          osg::Matrixd matrix;
          mapNode->getMapSRS()->getGeocentricSRS()->getEllipsoid()->computeLocalToWorldTransformFromLatLongHeight(osg::DegreesToRadians(30.58), osg::DegreesToRadians(119.99), 10, matrix);
          // 比例变换——放大一下
          matrix.preMult(osg::Matrixd::scale(osg::Vec3d(29, 25, 25)));
          // transform节点
          scenicSpot = new osg::MatrixTransform;
          scenicSpot->setMatrix(matrix);
      
          // 设置光照
          osg::ref_ptr<osg::StateSet> state = scenicSpot->getOrCreateStateSet();
          state->setMode(GL_LIGHTING, osg::StateAttribute::ON);
          state->setMode(GL_LIGHT0, osg::StateAttribute::ON);
          state->setMode(GL_LIGHT1, osg::StateAttribute::ON);
          osg::ref_ptr<osg::LightSource> lightSource0 = new osg::LightSource;
          osg::ref_ptr<osg::Light>light0 = new osg::Light;
          light0->setLightNum(0);
          light0->setDirection(osg::Vec3(0, -1, 0));
          light0->setPosition(osg::Vec4(-4817129, -5391926, -141700, 0));
          light0->setDiffuse(osg::Vec4(1.0, 1.0, 0.8, 1));
          lightSource0->setLight(light0);
          osg::ref_ptr<osg::LightSource> lightSource1 = new osg::LightSource;
          osg::ref_ptr<osg::Light>light1 = new osg::Light;
          light1->setLightNum(0);
          light1->setDirection(osg::Vec3(0, 1, 0));
          light1->setPosition(osg::Vec4(-4817129, 5391926, -141700, 0));
          light1->setDiffuse(osg::Vec4(1.0, 1.0, 0.9, 1));
          lightSource1->setLight(light1);
    
          // 读取osg文件
          osg::ref_ptr<osg::Node>cow = osgDB::readNodeFile("./data/model/cow.osg");
          // osg中光照只会对有法线的模型起作用,而模型经过缩放后法线不变。
          // 所以需要手动设置属性,让法线随着模型大小变化而变化。
          // If enabled, normal vectors specified with glNormal are scaled to unit length after transformation.
          cow->getOrCreateStateSet()->setMode(GL_RESCALE_NORMAL, osg::StateAttribute::ON);
          scenicSpot->addChild(cow);
          scenicSpot->addChild(lightSource0);
          scenicSpot->addChild(lightSource1);
          sceneRoot->addChild(scenicSpot);
    }
    

操作osgEarth的元素

  • earth文件的图层

    • osgEarth提供了ImageLayer来操作图层。

    • 通过mapNode获取earth文件信息。

      void COSGObject::initLayer()
      {
      	// 从earth文件中根据name获得图层
      	globeLayer = mapNode->getMap()->getLayerByName<osgEarth::ImageLayer>("globeImage");
      	provinceBoundaryLayer = mapNode->getMap()->getLayerByName<osgEarth::ImageLayer>("provinceBoundary");
      	countryBoundaryLayer = mapNode->getMap()->getLayerByName<osgEarth::ImageLayer>("countryBoundary");
      	contourLayer = mapNode->getMap()->getLayerByName<osgEarth::ImageLayer>("contour");
      	detailLayer = mapNode->getMap()->getLayerByName<osgEarth::ImageLayer>("detail");
      }
      
    • 用ImageLayer对象操作图层的属性,如透明度。

      void COSGObject::setProvinceBoundaryOpacity(float opacity)
      {
      	if (provinceBoundaryLayer.valid())
      	{
      		provinceBoundaryLayer->setOpacity(opacity);
      	}
      }
      void COSGObject::enableProvinceBoundary(bool flag)
      {
      	provinceBoundaryLayer->setVisible(flag);
      }
      
  • OSG节点的显示

    • 所有节点都继承自Node类。

    • 将NodeMask设置为0可让节点及其子节点不可见。

      void COSGObject::enableNationFlag(BOOL flag)
      {
      	nationFlag->getChild(0)->setNodeMask(flag);
      }
      

坐标计算

  • 经纬度

    • 调用Viewer类的computeIntersections函数,求摄像头与焦点连线与地球的交点。

    • 得到交点后转换为经纬度并显示在编辑框中。

    // 求交点
    if (viewer->computeIntersections(eventAdapter, res))
    {
        osgUtil::LineSegmentIntersector::Intersection first = *res.begin();
        // 取出交点坐标
        osg::Vec3d point = first.getWorldIntersectPoint();
        double latitude, longitude, altitude;
        // 转经纬度
        osg::ref_ptr<osg::EllipsoidModel> ellipsoidModel = new osg::EllipsoidModel;
        ellipsoidModel->convertXYZToLatLongHeight(point.x(), point.y(), point.z(), latitude, longitude, altitude);
        latitude = osg::RadiansToDegrees(latitude);
        longitude= osg::RadiansToDegrees(longitude);
        CString str;
        str.Format(_T("%.2lf"), longitude);
        latitudeEdit->SetEditText(str);
        str.Format(_T("%.2lf"), latitude);
        longitudeEdit->SetEditText(str);
        str.Format(_T("%.2lfm"), altitude);
        altitudeEdit->SetEditText(str);
    }
    
  • 视点

    • 通过Viewer得到操作器,再获取当前视点。

    • 视点的focalPoint即为焦点。

    // 获取视点
      osgEarth::Viewpoint viewpoint = dynamic_cast<osgEarth::Util::EarthManipulator*>(viewer->getCameraManipulator())->getViewpoint();
      CString str;
      osgEarth::GeoPoint pos = viewpoint.focalPoint().value();
      
      str.Format(_T("%.2lf"), pos.x());
      viewLongitudeEdit->SetEditText(str);
      str.Format(_T("%.2lf"), pos.y());
      viewLatitudeEdit->SetEditText(str);
      str.Format(_T("%.2lf"), pos.z());
      viewZEdit->SetEditText(str);
      str.Format(_T("%.2lf"), viewpoint.getHeading());
      viewHeadingEdit->SetEditText(str);
      str.Format(_T("%.2lf"), viewpoint.getPitch());
      viewPitchEdit->SetEditText(str);
      str.Format(_T("%.2lf"), viewpoint.getRange());
      viewRangeEdit->SetEditText(str);
    
  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值