osgEarth改变投影方式(2D/3D互转),实现二三维数据同步

12 篇文章 1 订阅
3 篇文章 0 订阅
文章讨论了如何在osgEarth中通过代码和.earth文件加载同一文件时,实现二维和三维视图的同步,包括Map和Layer的投影设置,以及如何在已加载三维模型后切换到二维并保持数据同步。
摘要由CSDN通过智能技术生成

项目场景:

想通过osgViewer::CompositeViewer添加同一个.earth文件实现两个View一边显示二维一边显示三维,并且加载的shp之类的数据完全同步。

osgEarth有两种方式构建MapNode,一是通过.earth文件,二是通过代码。

通过代码方式示例如下(官方例子Example osgearth_minimap):

MapNode* makeMiniMapNode( )
{
    Map* map = new Map();
    map->setProfile(Profile::create(Profile::SPHERICAL_MERCATOR));

    // add a semi-transparent XYZ layer:
    XYZImageLayer* osm = new XYZImageLayer();
    //osm->setURL("http://[abc].tile.openstreetmap.org/{z}/{x}/{y}.png");
    osm->setURL("https://gac-geo.googlecnapps.cn/maps/vt?lyrs=y&gl=cn&x={x}&y={y}&z={z}");
    osm->setProfile(Profile::create(Profile::SPHERICAL_MERCATOR));
    map->addLayer(osm);

    TerrainOptions terrainOptions;
    terrainOptions.lodMethod() = TerrainLODMethod::SCREEN_SPACE;

    MapNode::Options mapNodeOptions;
    mapNodeOptions.terrain() = terrainOptions;

    MapNode* mapNode = new MapNode(map, mapNodeOptions);
    mapNode->setEnableLighting(false);

    return mapNode;
}

 通过.earth文件示例如下:

方式1:

通过VS属性管理里调试菜单指定命令参数。

//官方示例    
auto node = MapNodeHelper().load( arguments, &viewer );
if (node.valid())
{
    MapNode* mapNode = MapNode::get(node);
        if (!mapNode)
            return -1;
}

 方式2:

直接使用osgDB::readNodeFile()方法进行动态转换。

m_p2DMapNode = dynamic_cast<osgEarth::MapNode*>(osgDB::readNodeFile(strEarthFile));

问题描述

通过代码不难看出,通过Map和Layer各自的setProfile方法分别为地图和图层指定投影,不难想象,如果地图和图层的投影方式不一致时会将图层进行重投影。

通过.earth文件也是类似,通过<options>参数里的<profile>指定Map的投影,通过<XYZImage name="osm_mapnik">类似节点里的<profile>指定Layer的投影,如果地图和图层的投影方式不一致时会将图层进行重投影。

但是本文要解决的问题,是无论是通过代码还是.earth文件构建MapNode,并且已经构建成功后,如何修改投影,例如MapNode原本是三维现在转换为二维。或者原本是二维转换为三维。

通过代码加载方式的提示可以进行如下操作:

注:原本的MapNode是三维的球

m_p2DMapNode = dynamic_cast<osgEarth::MapNode*>(osgDB::readNodeFile(strEarthFile));
if (m_p2DMapNode)
{
    m_p2DMapNode->getMap()->setProfile(osgEarth::Profile::create(osgEarth::Profile::PLATE_CARREE));
}

结果如下: 

 

不难看出,代码确实可行,但是其实还有潜藏的问题 。

.earth文件:

<map name="locgis" type="geocentric" version="2" encoding="UTF-8">

    <image driver="gdal">
    <url>./data/world.tif</url>
    <profile>global-geodetic</profile>
    <visible>true</visible>
  </image>
  
<ArcGISServerImage name="World Imagery">
        <url>https://services.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer/</url>
        <nodata_image>https://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/100/0/0.jpeg</nodata_image>
    </ArcGISServerImage>

    <ArcGISServerImage name="Transportation" enabled="false">
        <url>https://services.arcgisonline.com/ArcGIS/rest/services/Reference/World_Transportation/MapServer</url>
    </ArcGISServerImage>

    <ArcGISServerImage name="Shaded Relief" enabled="false">
        <url>https://services.arcgisonline.com/arcgis/rest/services/World_Shaded_Relief/MapServer</url>
    </ArcGISServerImage>

    <ArcGISServerElevation name="Elevation layer" max_data_level="13" enabled="true">
        <url>https://services.arcgisonline.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer</url>
    </ArcGISServerElevation>
  
 <model name="buildings" driver="feature_geom">
             
        <features name="buildings" driver="ogr">
            <url>./data/chengdu.shp</url>
            <build_spatial_index>true</build_spatial_index>
        </features>
        
        <layout>
            <tile_size_factor>45</tile_size_factor>
            <level name="default" max_range="20000">
                <selector class="buildings"/>
            </level>
        </layout>
        
        <styles>            
            
            <style type="text/css">
                buildings {
                    extrusion-height:      3.5 * max([Floor], 1);
                    extrusion-flatten:     true;
                    extrusion-wall-style:  building-wall;
					extrusion-wall-gradient: 0.5;
                    extrusion-roof-style:  building-rooftop;
                    altitude-clamping:     terrain;
					altitude-technique:      map;
                    altitude-binding:        vertex;
                }            
                <!-- building-wall { -->
                    <!-- skin-library:     none; -->
                    <!-- skin-tags:        building; -->
                    <!-- skin-random-seed: 1; -->
                    <!-- fill:             #ffffff; -->
                <!-- } -->
                <!-- building-rooftop { -->
                    <!-- skin-library:     none; -->
                    <!-- skin-tags:        rooftop; -->
                    <!-- skin-tiled:       true; -->
                    <!-- skin-random-seed: 1; -->
                    <!-- fill:             #ffffff; -->
                <!-- } -->
            </style>
			
						
		    <!--Exclude certain buildings from being rendered b/c they will be replaced with geospecific buildings -->
			<!-- <selector class="buildings"> -->
                <!-- <query> -->
                    <!-- <expr><![CDATA[ OBJECTID_1 <> 91506 and OBJECTID_1 <> 12921 and OBJECTID_1 <> 11460 and OBJECTID_1 <> 11474 and OBJECTID_1 <> 11471 and OBJECTID_1 <> 11439 and OBJECTID_1 <> 11432 and  OBJECTID_1 <> 91499 and OBJECTID_1 <> 10878 ]]> </expr> -->
                <!-- </query> -->
            <!-- </selector>			 -->
			
        </styles>

        <lighting>false</lighting>        
    </model>
  
  <!-- <elevation driver="tms"> -->
    <!-- <url>./data/12/</url> -->
    <!-- <profile>global-geodetic</profile> -->
	<!-- <visible>true</visible> -->
  <!-- </elevation> -->
  
  <options>
    <!--cache_policy usage="cache_only"/-->
    <cache_policy usage="read_write"/>
    <elevation_tile_size>15</elevation_tile_size>
    
    <terrain>
      <first_lod>2</first_lod>
      <mind_lod>19</mind_lod>
      <min_tile_range_factor>7</min_tile_range_factor>
    </terrain>
    
  </options>
  
</map>

 三维效果:

二维效果:

 使用osgViewer::CompositeViewer同时添加二三维做个对比:

 将三维转为二维后.earth下添加的shp文件并没有渲染出来。


原因分析:

Map下添加的Layer的投影并没有改变,即Map的setProfile方法虽然可以改变Map的投影方式但并不会改变其下Layer的投影方式。

 osgEarth源码:

void
Map::setProfile(const Profile* value)
{
    bool notifyLayers = !_profile.valid();

    if (value)
    {
        _profile = value;

        // create a "proxy" profile to use when querying elevation layers with a vertical datum
        if (_profile.valid() && _profile->getSRS()->getVerticalDatum() != 0L )
        {
            ProfileOptions po = _profile->toProfileOptions();
            po.vsrsString().unset();
            _profileNoVDatum = Profile::create(po);
        }
        else
        {
            _profileNoVDatum = _profile;
        }

        // finally, fire an event if the profile has been set.
        OE_INFO << LC << "Map profile is: " << _profile->toString() << std::endl;
    }

    // If we just set the profile, tell all our layers they are now added
    // to a valid map.
    if (_profile.valid() && notifyLayers)
    {
        for(LayerVector::iterator i = _layers.begin(); i != _layers.end(); ++i)
        {
            Layer* layer = i->get();
            if (layer->isOpen())
            {
                layer->addedToMap(this);
            }
        }
    }
}

解决方案:

还是通过代码添加MapNode的提示,把图层重新添加一次,通过添加图层时会对图层重投影来改变Layer的投影。

代码如下:

osgEarth::MapNode* p2DMapNode = dynamic_cast<osgEarth::MapNode*>(osgDB::readNodeFile(strEarthFile));

p2DMapNode->getMap()->setProfile(osgEarth::Profile::create(osgEarth::Profile::PLATE_CARREE));
osgEarth::LayerVector layers;
p2DMapNode->getMap()->getLayers(layers);
for (auto itr : layers)
{
	p2DMapNode->getMap()->removeLayer(itr);
}
p2DMapNode->getMap()->addLayers(layers);

结果展示: 

 这样有什么作用呢?就是可以实现二三维加载同一个.earth文件,实现二三维数据同步。

完整代码:

#include <osgDB/ReadFile>
#include <osgEarth/Common>
#include <osgEarth/EarthManipulator>
#include <osgViewer/CompositeViewer>
#include <osgEarth/GLUtils>
#include <osgEarth/Registry>
#include <osgEarth/GeoTransform>

#include <windows.h>

namespace osgEarth
{
	class ImageLayer;
}

const std::string strEarthFile = R"(E:\OSGEarth\osgearth\tests\chengdu_building.earth)";

int main()
{
	osgEarth::initialize();

	osgViewer::CompositeViewer viewer;
	viewer.setThreadingModel(viewer.SingleThreaded);// 设置单线程

	osgViewer::View* p3DView = new osgViewer::View();
	p3DView->getDatabasePager()->setUnrefImageDataAfterApplyPolicy(true, false);
	p3DView->getCamera()->setNearFarRatio(0.00002);

	osgEarth::EarthManipulator* p3DEarthManipulator = new osgEarth::EarthManipulator();
	p3DView->setCameraManipulator(p3DEarthManipulator);
	p3DView->setUpViewInWindow(50, 50, 1600, 800, 0);

	p3DView->getCamera()->setViewport(800, 0, 800, 800);
	p3DView->getCamera()->setProjectionMatrixAsOrtho2D(osgEarth::MERC_MINX, osgEarth::MERC_MAXX, osgEarth::MERC_MINY, osgEarth::MERC_MAXY);

	viewer.addView(p3DView);

	osgViewer::View* p2DView = new osgViewer::View();
	p2DView->getDatabasePager()->setUnrefImageDataAfterApplyPolicy(true, false);
	p2DView->getCamera()->setNearFarRatio(0.00002);
	osgEarth::EarthManipulator* p2DEarthManipulator = new osgEarth::EarthManipulator();
	p2DEarthManipulator->getSettings()->bindMouse(osgEarth::Util::EarthManipulator::ACTION_NULL, osgGA::GUIEventAdapter::MIDDLE_MOUSE_BUTTON, 0);
	p2DView->setCameraManipulator(p2DEarthManipulator);

	p2DView->getCamera()->setViewport(0, 0, 800, 800);
	p2DView->getCamera()->setProjectionMatrixAsOrtho2D(osgEarth::MERC_MINX, osgEarth::MERC_MAXX, osgEarth::MERC_MINY, osgEarth::MERC_MAXY);
	p2DView->getCamera()->setGraphicsContext(p3DView->getCamera()->getGraphicsContext());
	osgEarth::GLUtils::setGlobalDefaults(p3DView->getCamera()->getOrCreateStateSet());
	viewer.addView(p2DView);

	osgEarth::MapNode* p3DMapNode = dynamic_cast<osgEarth::MapNode*>(osgDB::readNodeFile(strEarthFile));
	p3DMapNode->setEnableLighting(false);
	osg::Group* p3MapGroup = new osg::Group;
	p3MapGroup->addChild(p3DMapNode);
	p3DView->setSceneData(p3MapGroup);

	osgEarth::MapNode* p2DMapNode = dynamic_cast<osgEarth::MapNode*>(osgDB::readNodeFile(strEarthFile));

	p2DMapNode->getMap()->setProfile(osgEarth::Profile::create(osgEarth::Profile::PLATE_CARREE));
	osgEarth::LayerVector layers;
	p2DMapNode->getMap()->getLayers(layers);
	for (auto itr : layers)
	{
		p2DMapNode->getMap()->removeLayer(itr);
	}
	p2DMapNode->getMap()->addLayers(layers);

	osg::Group* p2DMapGroup = new osg::Group;
	p2DMapGroup->addChild(p2DMapNode);
	p2DView->setSceneData(p2DMapGroup);

	viewer.run();
}

运行结果:

  • 9
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 16
    评论
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值