• osgEarth示例分析——osgearth_cluster


    前言

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

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

    执行效果

    执行命令如下:

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

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

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

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

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

     代码分析

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

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

    一、性质不同

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

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

    二、作用不同

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

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

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

    代码分析:

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. #include
    11. #include
    12. #define LC "[viewer] "
    13. using namespace osgEarth;
    14. using namespace osgEarth::Util;
    15. using namespace osgEarth::Annotation;
    16. int
    17. usage(const char* name)
    18. {
    19. OE_NOTICE
    20. << "\nUsage: " << name << " file.earth" << std::endl
    21. << MapNodeHelper().usage() << std::endl;
    22. return 0;
    23. }
    24. // 在extent的包围盒内,创建placeNode,1000=count,存储在nodes中
    25. void makePlaces(MapNode* mapNode, unsigned int count, const GeoExtent& extent, osg::NodeList& nodes)
    26. {
    27. // set up a style to use for placemarks:
    28. Style placeStyle;
    29. // 关闭清除
    30. placeStyle.getOrCreate()->declutter() = false;
    31. // A lat/long SRS for specifying points.获取地理坐标系
    32. const SpatialReference* geoSRS = mapNode->getMapSRS()->getGeographicSRS();
    33. //--------------------------------------------------------------------
    34. {
    35. osg::ref_ptr pin = osgDB::readRefImageFile("icon.png");
    36. for (unsigned int i = 0; i < count; i++)
    37. {
    38. // 随机生成经纬度
    39. double lat = extent.yMin() + extent.height() * (rand() * 1.0) / (RAND_MAX - 1);
    40. double lon = extent.xMin() + extent.width() * (rand() * 1.0) / (RAND_MAX - 1);
    41. PlaceNode* place = new PlaceNode("Placemark", placeStyle, pin.get());
    42. place->setPosition(GeoPoint(geoSRS, lon, lat, 0.0));// 贴地
    43. place->setMapNode(mapNode);
    44. place->setDynamic(true);// 动态开启
    45. nodes.push_back(place);
    46. }
    47. }
    48. }
    49. // 创建模型,传入的map节点、数量、在包围盒范围、模型node列表
    50. void makeModels(MapNode* mapNode, unsigned int count, const GeoExtent& extent, osg::NodeList& nodes)
    51. {
    52. // 读取模型,长宽高方向扩大的倍数???
    53. osg::ref_ptr< osg::Node > cessna = osgDB::readRefNodeFile("cessna.osg.10,10,10.scale");
    54. osg::ref_ptr< osg::Node > cow = osgDB::readRefNodeFile("cow.osg.100,100,100.scale");
    55. osgEarth::Registry::shaderGenerator().run(cessna.get());// 采用 默认着色器生成器
    56. osgEarth::Registry::shaderGenerator().run(cow.get());
    57. // A lat/long SRS for specifying points.地理坐标系
    58. const SpatialReference* geoSRS = mapNode->getMapSRS()->getGeographicSRS();
    59. bool useCow = false;
    60. for (unsigned int i = 0; i < count; i++)
    61. {
    62. // 随机生成经纬度
    63. double lat = extent.yMin() + extent.height() * (rand() * 1.0) / (RAND_MAX - 1);
    64. double lon = extent.xMin() + extent.width() * (rand() * 1.0) / (RAND_MAX - 1);
    65. // 接受地理空间坐标的变换节点 transform
    66. GeoTransform* transform = new GeoTransform();
    67. // 高度值默认1000
    68. transform->setPosition(GeoPoint(geoSRS, lon, lat, 1000));
    69. if (useCow)
    70. {
    71. transform->addChild(cow.get());
    72. transform->setName("cow");
    73. }
    74. else
    75. {
    76. transform->addChild(cessna.get());
    77. transform->setName("plane");
    78. }
    79. nodes.push_back(transform);
    80. useCow = !useCow;// 两个模型交替绘制
    81. }
    82. }
    83. // 将view传入,创建控制面板容器
    84. Container*
    85. createControlPanel(osgViewer::View* view)
    86. {
    87. ControlCanvas* canvas = ControlCanvas::getOrCreate(view);
    88. VBox* vbox = canvas->addControl(new VBox());// 垂直box
    89. vbox->setChildSpacing(10);
    90. return vbox;
    91. }
    92. // 设置半径控制事件,主要是改变clusterNode的包围盒半径
    93. struct SetRadius : public ControlEventHandler
    94. {
    95. SetRadius(ClusterNode* clusterNode) :
    96. _clusterNode( clusterNode )
    97. { }
    98. void onValueChanged(Control* control, float value)
    99. {
    100. _clusterNode->setRadius(value);
    101. }
    102. ClusterNode* _clusterNode;
    103. };
    104. // 添加图标
    105. struct AddIcons : public ControlEventHandler
    106. {
    107. AddIcons(ClusterNode* clusterNode, MapNode* mapNode) :
    108. _clusterNode(clusterNode),
    109. _mapNode(mapNode)
    110. { }
    111. // 点击button时
    112. void onClick(Control* button)
    113. {
    114. osg::NodeList nodes;
    115. GeoExtent extent(SpatialReference::create("wgs84"), -180, -90, 180, 90);
    116. makePlaces(_mapNode, 1000, extent, nodes);// 在extent的包围盒内,创建placeNode
    117. std::cout << "PlaceNode nodes.size() " << nodes.size() << std::endl;
    118. for (unsigned int i = 0; i < nodes.size(); ++i)
    119. {
    120. _clusterNode->addNode(nodes[i].get());
    121. }
    122. }
    123. ClusterNode* _clusterNode;
    124. MapNode* _mapNode;
    125. };
    126. // 是否clusterNode使能
    127. struct ToggleEnabled : public ControlEventHandler
    128. {
    129. ToggleEnabled(ClusterNode* clusterNode) :
    130. _clusterNode(clusterNode)
    131. { }
    132. virtual void onValueChanged(Control* control, bool value) {
    133. _clusterNode->setEnabled(value);// 像碎玻璃,关闭后,所有文本、模型都变为点
    134. }
    135. ClusterNode* _clusterNode;
    136. };
    137. // 创建控件
    138. void buildControls(Container* container, ClusterNode* clusterNode, MapNode* mapNode)
    139. {
    140. // the outer container:
    141. // new 网格容器
    142. Grid* grid = container->addControl(new Grid());
    143. grid->setBackColor(0, 0, 0, 0.5);// 半透明黑色背景
    144. grid->setMargin(10);// 外边距
    145. grid->setPadding(10);// 内边距
    146. grid->setChildSpacing(10);// 子控件间的距离
    147. grid->setChildVertAlign(Control::ALIGN_CENTER);// 子控件垂直居中
    148. grid->setAbsorbEvents(true);// 接收事件
    149. grid->setVertAlign(Control::ALIGN_TOP);// 网格控件
    150. // Radius 控制半径的标签
    151. LabelControl* radiusLabel = new LabelControl("Radius");
    152. radiusLabel->setVertAlign(Control::ALIGN_CENTER);
    153. grid->setControl(0, 0, radiusLabel);
    154. // 水平滑动条控件,进而改变clusterNode的包围盒半径
    155. HSliderControl* radiusAdjust = new HSliderControl(1, 500, clusterNode->getRadius(), new SetRadius(clusterNode));
    156. radiusAdjust->setWidth(125);
    157. radiusAdjust->setHeight(12);
    158. radiusAdjust->setVertAlign(Control::ALIGN_CENTER);
    159. grid->setControl(1, 0, radiusAdjust);
    160. // 将radiusAdiust的value值,写在lable控件上,用于显示半径
    161. grid->setControl(2, 0, new LabelControl(radiusAdjust));
    162. // 第1行第2列label控件
    163. grid->setControl(0, 1, new LabelControl("Enabled"));
    164. CheckBoxControl* checkBox = new CheckBoxControl(clusterNode->getEnabled());
    165. checkBox->setHorizAlign(Control::ALIGN_LEFT);
    166. checkBox->addEventHandler(new ToggleEnabled(clusterNode));// 切换是否clusterNode使能事件
    167. grid->setControl(1, 1, checkBox);// 第二行第二列
    168. // 1行3列,按钮,加图标事件,灰色的按钮,鼠标移动上去,会变为蓝色按钮
    169. grid->setControl(0, 2, new ButtonControl("Add Icons", new AddIcons(clusterNode, mapNode)));
    170. }
    171. //! Displays a simplified count for the cluster instead of the exact number.
    172. // 简单计算个数的回调方法
    173. class SimplifyCountCallback : public ClusterNode::StyleClusterCallback
    174. {
    175. public:
    176. virtual void operator()(ClusterNode::Cluster& cluster)
    177. {
    178. if (cluster.nodes.size() >= 100)
    179. {
    180. cluster.marker->setText("100+");
    181. }
    182. else if (cluster.nodes.size() >= 50)
    183. {
    184. cluster.marker->setText("50+");
    185. }
    186. else if (cluster.nodes.size() >= 25)
    187. {
    188. cluster.marker->setText("25+");
    189. }
    190. else if (cluster.nodes.size() >= 10)
    191. {
    192. cluster.marker->setText("10+");
    193. }
    194. else
    195. {
    196. cluster.marker->setText("2+");
    197. }
    198. }
    199. };
    200. //! Changes the name of a marker based on the name of the clustered nodes.
    201. class StyleByNameCallback : public ClusterNode::StyleClusterCallback
    202. {
    203. public:
    204. StyleByNameCallback()
    205. {
    206. _planeImage = osgDB::readRefImageFile("airport.png");
    207. _cowImage = osgDB::readRefImageFile("hospital.png");
    208. }
    209. virtual void operator()(ClusterNode::Cluster& cluster)
    210. {
    211. std::stringstream buf;
    212. buf << cluster.nodes[0]->getName() << "(" << cluster.nodes.size() << ")" << std::endl;
    213. cluster.marker->setText(buf.str());
    214. if (cluster.nodes[0]->getName() == "plane")
    215. {
    216. cluster.marker->setIconImage(_planeImage.get());
    217. }
    218. else if (cluster.nodes[0]->getName() == "cow")
    219. {
    220. cluster.marker->setIconImage(_cowImage.get());
    221. }
    222. }
    223. osg::ref_ptr< osg::Image > _planeImage;
    224. osg::ref_ptr< osg::Image > _cowImage;
    225. };
    226. //! Only allows nodes with the same name to be clustered together.
    227. class ClusterByNameCallback : public ClusterNode::CanClusterCallback
    228. {
    229. public:
    230. virtual bool operator()(osg::Node* a, osg::Node* b)
    231. {
    232. return (a->getName() == b->getName());
    233. }
    234. };
    235. int
    236. main(int argc, char** argv)
    237. {
    238. osg::ArgumentParser arguments(&argc, argv);
    239. // help?
    240. if (arguments.read("--help"))
    241. return usage(argv[0]);
    242. // create a viewer:
    243. osgViewer::Viewer viewer(arguments);
    244. //Create the control panel 创建控制面板
    245. Container* container = createControlPanel(&viewer);
    246. // Tell the database pager to not modify the unref settings
    247. // 通知数据库paper不要修改不相关设置
    248. viewer.getDatabasePager()->setUnrefImageDataAfterApplyPolicy(true, false);
    249. // install our default manipulator (do this before calling load)
    250. // 安装默认操作器,此时操作器的参数从arguments中读取
    251. viewer.setCameraManipulator(new EarthManipulator(arguments));
    252. // disable the small-feature culling
    253. viewer.getCamera()->setSmallFeatureCullingPixelSize(-1.0f);
    254. // set a near/far ratio that is smaller than the default. This allows us to get
    255. // closer to the ground without near clipping. If you need more, use --logdepth
    256. // 将 近远率小于默认值,不需要剪裁就能离地面更近,如果需要更近,则设置 --logdepth(在cmd中设置)
    257. viewer.getCamera()->setNearFarRatio(0.0001);
    258. // load an earth file, and support all or our example command-line options
    259. // and earth file tags
    260. // earth文件中,需要有 external 额外的标签,用来设置额外属性
    261. osg::Node* node = MapNodeHelper().load(arguments, &viewer);
    262. if (node)
    263. {
    264. MapNode* mapNode = MapNode::findMapNode(node);
    265. osg::NodeList nodes;
    266. //GeoExtent extent(SpatialReference::create("wgs84"), -180, -90, 180, 90);
    267. // 坐标系,西,南,东,北。构造GeoExtent.
    268. // 在给定的坐标系和坐标下,定义包围盒。
    269. GeoExtent extent(SpatialReference::create("wgs84"), -160.697021484375, 18.208480196039883, -153.951416015625, 22.978623970384913);
    270. // 创建模型,数量总共10000个,nodes.push_back(transform);transform中有10000个模型
    271. makeModels(mapNode, 10000, extent, nodes);
    272. // ClusterNode将重叠的节点聚集到屏幕上的PlaceNode中,以避免视觉混乱并提高性能。
    273. ClusterNode* clusterNode = new ClusterNode(mapNode, osgDB::readImageFile("../data/placemark32.png"));
    274. clusterNode->setStyleCallback(new StyleByNameCallback());// 设置名称回调
    275. clusterNode->setCanClusterCallback(new ClusterByNameCallback());// 设置是否允许回调操作
    276. std::cout << "nodes.size() = " << nodes.size() << std::endl;
    277. for (unsigned int i = 0; i < nodes.size(); i++)
    278. {
    279. clusterNode->addNode(nodes[i].get());
    280. }
    281. mapNode->addChild(clusterNode);
    282. // 创建控制面板
    283. buildControls(container, clusterNode, mapNode);
    284. viewer.setSceneData(node);
    285. while (!viewer.done())
    286. {
    287. viewer.frame();
    288. }
    289. return viewer.run();
    290. }
    291. else
    292. {
    293. return usage(argv[0]);
    294. }
    295. return 0;
    296. }

  • 相关阅读:
    适用于 Linux 和 Unix 的特权访问管理
    软件测试之TCP、HTTP协议必知必会,面试必备
    基于PHP MYSQL的化妆品店会员管理网站的设计与实现毕业设计源码131102
    R包的尽头是 C/C++
    数据结构中的堆(Java)
    泰语快速学习方法!速成方法学习!
    【机器学习300问】38、什么是K-means算法?
    深入理解Golang之Map
    什么是大模型微调?微调的分类、方法、和步骤
    leetcode - 319. Bulb Switcher
  • 原文地址:https://blog.csdn.net/qq_34732729/article/details/128183626