项目场景:
想通过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();
}
运行结果:
文章来源:https://www.toymoban.com/news/detail-838683.html
文章来源地址https://www.toymoban.com/news/detail-838683.html
到了这里,关于osgEarth改变投影方式(2D/3D互转),实现二三维数据同步的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!