osgEarth示例分析——osgearth_cluster
创始人
2024-03-20 10:39:45
0

前言

osgearth_cluster示例,展示了绘制很多模型时,如何合适设置模型遮挡的混乱情况。

当模型过多时,可以创建 osgEarth::Util::ClusterNode 节点对象,然后创建 osg::NodeList,将需要绘制的节点 node 们,都 push_back 到 osg::NodeList 中,然后将 osg::NodeList 添加 addChild 到 osgEarth::Util::ClusterNode 中,通过控制 osgEarth::Util::ClusterNode 的半径范围,来控制节点node们的显示密集程度。

执行效果

执行命令如下:

// 简单的earth文件即可
osgearth_clusterd.exe earth_image\world.earth

绘制10000个飞机和牛的模型,并且为其添加icon图标。当距离较远时,会显示图标。拉进后,才现实模型。

 Enabled的复选框被取消勾选后,图上的模型、图标、文本,都会消失,取而代之的是culler,像玻璃碎渣。

 当扩大半径值时,所有的模型看起来稀疏了不少。但数量并没有改变。只是距离相近的相同的模型用1个图标显示,并且文本中会显示当前标签下,有多少个模型。类似地图中显示的那样。视觉上更友好些。

 Enabled的下方灰色矩形是按钮(自带按钮确实丑了点)。点击按钮,会随机生成很多PlaceNode。这些黄色的图标,也会随着视距增加,合并成一个小的图标,比如飞机或者医院的图标,然后显示当前隐藏的黄色图标的个数。

 代码分析

额外先分析一下坐标系,毕竟经常会用到。

// 通过mapNode获取到空间坐标系类
osgEarth::SpatialReference* geoSRS = mapNode->getMapSRS();// 在 osgEarth::SpatialReference 类中,有几种获取坐标系的方式:// 地理坐标系,一般高度。
getGeographicSRS();// 大地测量坐标系,获取内容同上,但是z值为:大地椭球之上的z值.
// 当存在地形数据时,此方法用的较多。
getGeodeticSRS();// 获取与此SRS椭球体关联的地心参考系。代码中,用的较少
getGeocentricSRS();

一、性质不同

1、地理坐标系(GeographicCoordinateSystem),是使用三维球面来定义地球表面位置,以实现通过经纬度对地球表面点位引用的坐标系。

2、大地坐标系是大地测量中以参考椭球面为基准面建立起来的坐标系。

二、作用不同

1、地理坐标系:定义了地表点位的经纬度,并且根据其所采用的参考椭球体参数还可求得点位的绝对高程值。

2、大地坐标系:是大地测量的基本坐标系,它是大地测量计算,地球形状大小研究和地图编制等的基础。

参考链接:地理坐标系和大地坐标系的区别_百度知道

代码分析:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include #include #include #define LC "[viewer] "using namespace osgEarth;
using namespace osgEarth::Util;
using namespace osgEarth::Annotation;int
usage(const char* name)
{OE_NOTICE<< "\nUsage: " << name << " file.earth" << std::endl<< MapNodeHelper().usage() << std::endl;return 0;
}// 在extent的包围盒内,创建placeNode,1000=count,存储在nodes中
void makePlaces(MapNode* mapNode, unsigned int count, const GeoExtent& extent, osg::NodeList& nodes)
{    // set up a style to use for placemarks:Style placeStyle;    // 关闭清除placeStyle.getOrCreate()->declutter() = false;// A lat/long SRS for specifying points.获取地理坐标系const SpatialReference* geoSRS = mapNode->getMapSRS()->getGeographicSRS();//--------------------------------------------------------------------{osg::ref_ptr pin = osgDB::readRefImageFile("icon.png");for (unsigned int i = 0; i < count; i++){// 随机生成经纬度double lat = extent.yMin() + extent.height() * (rand() * 1.0) / (RAND_MAX - 1);double lon = extent.xMin() + extent.width() * (rand() * 1.0) / (RAND_MAX - 1);PlaceNode* place = new PlaceNode("Placemark", placeStyle, pin.get());place->setPosition(GeoPoint(geoSRS, lon, lat, 0.0));// 贴地place->setMapNode(mapNode);place->setDynamic(true);// 动态开启nodes.push_back(place);}}    
}// 创建模型,传入的map节点、数量、在包围盒范围、模型node列表
void makeModels(MapNode* mapNode, unsigned int count, const GeoExtent& extent, osg::NodeList& nodes)
{// 读取模型,长宽高方向扩大的倍数???osg::ref_ptr< osg::Node > cessna = osgDB::readRefNodeFile("cessna.osg.10,10,10.scale");osg::ref_ptr< osg::Node > cow = osgDB::readRefNodeFile("cow.osg.100,100,100.scale");osgEarth::Registry::shaderGenerator().run(cessna.get());// 采用 默认着色器生成器osgEarth::Registry::shaderGenerator().run(cow.get());// A lat/long SRS for specifying points.地理坐标系const SpatialReference* geoSRS = mapNode->getMapSRS()->getGeographicSRS();bool useCow = false;for (unsigned int i = 0; i < count; i++){// 随机生成经纬度double lat = extent.yMin() + extent.height() * (rand() * 1.0) / (RAND_MAX - 1);double lon = extent.xMin() + extent.width() * (rand() * 1.0) / (RAND_MAX - 1);// 接受地理空间坐标的变换节点 transformGeoTransform* transform = new GeoTransform();// 高度值默认1000transform->setPosition(GeoPoint(geoSRS, lon, lat, 1000));if (useCow){transform->addChild(cow.get());transform->setName("cow");}        else{transform->addChild(cessna.get());transform->setName("plane");}nodes.push_back(transform);useCow = !useCow;// 两个模型交替绘制}
}// 将view传入,创建控制面板容器
Container*
createControlPanel(osgViewer::View* view)
{ControlCanvas* canvas = ControlCanvas::getOrCreate(view);VBox* vbox = canvas->addControl(new VBox());// 垂直boxvbox->setChildSpacing(10);return vbox;
}// 设置半径控制事件,主要是改变clusterNode的包围盒半径
struct SetRadius : public ControlEventHandler
{SetRadius(ClusterNode* clusterNode) :_clusterNode( clusterNode ){ }void onValueChanged(Control* control, float value){_clusterNode->setRadius(value);}ClusterNode* _clusterNode;    
};// 添加图标
struct AddIcons : public ControlEventHandler
{AddIcons(ClusterNode* clusterNode, MapNode* mapNode) :_clusterNode(clusterNode),_mapNode(mapNode){ }// 点击button时void onClick(Control* button){osg::NodeList nodes;GeoExtent extent(SpatialReference::create("wgs84"), -180, -90, 180, 90);makePlaces(_mapNode, 1000, extent, nodes);// 在extent的包围盒内,创建placeNodestd::cout << "PlaceNode nodes.size() " << nodes.size() << std::endl;for (unsigned int i = 0; i < nodes.size(); ++i){_clusterNode->addNode(nodes[i].get());}}ClusterNode* _clusterNode;MapNode* _mapNode;
};// 是否clusterNode使能
struct ToggleEnabled : public ControlEventHandler
{ToggleEnabled(ClusterNode* clusterNode) :_clusterNode(clusterNode){ }virtual void onValueChanged(Control* control, bool value) {_clusterNode->setEnabled(value);// 像碎玻璃,关闭后,所有文本、模型都变为点}ClusterNode* _clusterNode;
};// 创建控件
void buildControls(Container* container, ClusterNode* clusterNode, MapNode* mapNode)
{// the outer container: // new 网格容器Grid* grid = container->addControl(new Grid());grid->setBackColor(0, 0, 0, 0.5);// 半透明黑色背景grid->setMargin(10);// 外边距grid->setPadding(10);// 内边距grid->setChildSpacing(10);// 子控件间的距离grid->setChildVertAlign(Control::ALIGN_CENTER);// 子控件垂直居中grid->setAbsorbEvents(true);// 接收事件grid->setVertAlign(Control::ALIGN_TOP);// 网格控件// Radius 控制半径的标签LabelControl* radiusLabel = new LabelControl("Radius");radiusLabel->setVertAlign(Control::ALIGN_CENTER);grid->setControl(0, 0, radiusLabel);// 水平滑动条控件,进而改变clusterNode的包围盒半径HSliderControl* radiusAdjust = new HSliderControl(1, 500, clusterNode->getRadius(), new SetRadius(clusterNode));radiusAdjust->setWidth(125);radiusAdjust->setHeight(12);radiusAdjust->setVertAlign(Control::ALIGN_CENTER);grid->setControl(1, 0, radiusAdjust);// 将radiusAdiust的value值,写在lable控件上,用于显示半径grid->setControl(2, 0, new LabelControl(radiusAdjust));// 第1行第2列label控件grid->setControl(0, 1, new LabelControl("Enabled"));CheckBoxControl* checkBox = new CheckBoxControl(clusterNode->getEnabled());checkBox->setHorizAlign(Control::ALIGN_LEFT);checkBox->addEventHandler(new ToggleEnabled(clusterNode));// 切换是否clusterNode使能事件grid->setControl(1, 1, checkBox);// 第二行第二列// 1行3列,按钮,加图标事件,灰色的按钮,鼠标移动上去,会变为蓝色按钮grid->setControl(0, 2, new ButtonControl("Add Icons", new AddIcons(clusterNode, mapNode)));}//! Displays a simplified count for the cluster instead of the exact number.
// 简单计算个数的回调方法
class SimplifyCountCallback : public ClusterNode::StyleClusterCallback
{
public:virtual void operator()(ClusterNode::Cluster& cluster){        if (cluster.nodes.size() >= 100){cluster.marker->setText("100+");}else if (cluster.nodes.size() >= 50){cluster.marker->setText("50+");}else if (cluster.nodes.size() >= 25){cluster.marker->setText("25+");}else if (cluster.nodes.size() >= 10){cluster.marker->setText("10+");}else{cluster.marker->setText("2+");} }
};//! Changes the name of a marker based on the name of the clustered nodes.
class StyleByNameCallback : public ClusterNode::StyleClusterCallback
{
public:StyleByNameCallback(){_planeImage = osgDB::readRefImageFile("airport.png");_cowImage = osgDB::readRefImageFile("hospital.png");}virtual void operator()(ClusterNode::Cluster& cluster){    std::stringstream buf;buf << cluster.nodes[0]->getName() << "(" << cluster.nodes.size() << ")" << std::endl;cluster.marker->setText(buf.str());if (cluster.nodes[0]->getName() == "plane"){cluster.marker->setIconImage(_planeImage.get());}else if (cluster.nodes[0]->getName() == "cow"){cluster.marker->setIconImage(_cowImage.get());} }osg::ref_ptr< osg::Image > _planeImage;osg::ref_ptr< osg::Image > _cowImage;
};//! Only allows nodes with the same name to be clustered together.
class ClusterByNameCallback : public ClusterNode::CanClusterCallback
{
public:virtual bool operator()(osg::Node* a, osg::Node* b){return (a->getName() == b->getName());}
};int
main(int argc, char** argv)
{osg::ArgumentParser arguments(&argc, argv);// help?if (arguments.read("--help"))return usage(argv[0]);// create a viewer:osgViewer::Viewer viewer(arguments);//Create the control panel 创建控制面板    Container* container = createControlPanel(&viewer);// Tell the database pager to not modify the unref settings// 通知数据库paper不要修改不相关设置viewer.getDatabasePager()->setUnrefImageDataAfterApplyPolicy(true, false);// install our default manipulator (do this before calling load)// 安装默认操作器,此时操作器的参数从arguments中读取viewer.setCameraManipulator(new EarthManipulator(arguments));// disable the small-feature cullingviewer.getCamera()->setSmallFeatureCullingPixelSize(-1.0f);// set a near/far ratio that is smaller than the default. This allows us to get// closer to the ground without near clipping. If you need more, use --logdepth// 将 近远率小于默认值,不需要剪裁就能离地面更近,如果需要更近,则设置 --logdepth(在cmd中设置)viewer.getCamera()->setNearFarRatio(0.0001);// load an earth file, and support all or our example command-line options// and earth file  tags   // earth文件中,需要有 external 额外的标签,用来设置额外属性osg::Node* node = MapNodeHelper().load(arguments, &viewer);if (node){MapNode* mapNode = MapNode::findMapNode(node);osg::NodeList nodes;//GeoExtent extent(SpatialReference::create("wgs84"), -180, -90, 180, 90);// 坐标系,西,南,东,北。构造GeoExtent.// 在给定的坐标系和坐标下,定义包围盒。GeoExtent extent(SpatialReference::create("wgs84"), -160.697021484375, 18.208480196039883, -153.951416015625, 22.978623970384913);// 创建模型,数量总共10000个,nodes.push_back(transform);transform中有10000个模型makeModels(mapNode, 10000, extent, nodes);// ClusterNode将重叠的节点聚集到屏幕上的PlaceNode中,以避免视觉混乱并提高性能。ClusterNode* clusterNode = new ClusterNode(mapNode, osgDB::readImageFile("../data/placemark32.png"));clusterNode->setStyleCallback(new StyleByNameCallback());// 设置名称回调clusterNode->setCanClusterCallback(new ClusterByNameCallback());// 设置是否允许回调操作std::cout << "nodes.size() = " << nodes.size() << std::endl;for (unsigned int i = 0; i < nodes.size(); i++){clusterNode->addNode(nodes[i].get());}              mapNode->addChild(clusterNode);// 创建控制面板buildControls(container, clusterNode, mapNode);viewer.setSceneData(node);while (!viewer.done()){viewer.frame();}return viewer.run();}else{return usage(argv[0]);}return 0;
}

相关内容

热门资讯

汽车油箱结构是什么(汽车油箱结... 本篇文章极速百科给大家谈谈汽车油箱结构是什么,以及汽车油箱结构原理图解对应的知识点,希望对各位有所帮...
美国2年期国债收益率上涨15个... 原标题:美国2年期国债收益率上涨15个基点 美国2年期国债收益率上涨15个基...
嵌入式 ADC使用手册完整版 ... 嵌入式 ADC使用手册完整版 (188977万字)💜&#...
重大消息战皇大厅开挂是真的吗... 您好:战皇大厅这款游戏可以开挂,确实是有挂的,需要了解加客服微信【8435338】很多玩家在这款游戏...
盘点十款牵手跑胡子为什么一直... 您好:牵手跑胡子这款游戏可以开挂,确实是有挂的,需要了解加客服微信【8435338】很多玩家在这款游...
senator香烟多少一盒(s... 今天给各位分享senator香烟多少一盒的知识,其中也会对sevebstars香烟进行解释,如果能碰...
终于懂了新荣耀斗牛真的有挂吗... 您好:新荣耀斗牛这款游戏可以开挂,确实是有挂的,需要了解加客服微信8435338】很多玩家在这款游戏...
盘点十款明星麻将到底有没有挂... 您好:明星麻将这款游戏可以开挂,确实是有挂的,需要了解加客服微信【5848499】很多玩家在这款游戏...
总结文章“新道游棋牌有透视挂吗... 您好:新道游棋牌这款游戏可以开挂,确实是有挂的,需要了解加客服微信【7682267】很多玩家在这款游...
终于懂了手机麻将到底有没有挂... 您好:手机麻将这款游戏可以开挂,确实是有挂的,需要了解加客服微信【8435338】很多玩家在这款游戏...