前言
osgearth_splat示例,展示如何加载图片贴在地球上,且通过分类,贴文理绘制森林、草地等。
执行命令:osgearth_splatd.exe
效果
整个地球的影像,加载的一张贴图。黑白点点,是不同的贴图。当拉进放大后,才可以看清楚加载的是哪张纹理图。代码设置了15级。
代码分析
1. 创建 土地覆盖字典选项 LandCoverDictionaryOptions options;
将xml文件中的字典配置,读入options中,主要是name。
2. 用options初始化土地覆盖字典 LandCoverDictionary dictionary;
3. 设置gdal驱动 GDALOptions coverageDriver;,加载地形。
4. 创建LandCover的可序列化配置选项 LandCoverCoverageLayerOptions coverage;
主要包括驱动 coverageDriver 以及 map表(从xml读入,指定某位置放某类贴图)。
5. 为地图创建土地覆盖层:LandCoverLayer *landCover; 并将coverage设置到此土地覆盖层,指定最大层级。
6. 加载实际纹理映射定义,即map表中对应的分类,需要覆盖哪张图。创建 Surface* surface;通过 SplatCatalog* catalog 加载xml文件,完成name与纹理图的实际对应。
7. 创建Zone* splatZone;来指定surface图要覆盖到地图的哪个区域。
8. 创建 SplatLayer* splatLayer;将dictionary、landCover、splatZone设置到splatLayer 图层.
9. 有了上面的基础内容,可以通过直接加载image的方式创建图层。读取图片osg::ref_ptr<osg::Image> tree; 创建标牌 BillboardSymbol* treeSymbol ;将tree传入treeSymbol;
10. 创建土地覆盖种群选项,GroundCoverBiomeOptions forestBiome;
将 新物种 treeSymbol ,放置到 "forest" 种群中。
11. 创建 GroundCoverOptions treeOptions 地面覆盖选项,将生物种群 forestBiome 加入到 treeOptions中。同时设置 GroundCover* trees = new GroundCover(treeOptions);
12. 为要添加的 树 ,新增一个区域 Zone* treeZone; treeZone->setGroundCover(trees);
13. 为这些树,创建地面覆盖图层 GroundCoverLayer* treeLayer;并将内容设置进去。
- treeLayer->setLandCoverDictionary(dictionary);
- treeLayer->setLandCoverLayer(landCover);
- treeLayer->zones().push_back(treeZone);
14. 最后将上述设置,组装到map中。
- Map* map = new Map();
- map->addLayer(dictionary);
- map->addLayer(landCover);
- map->addLayer(splatLayer);
【注】其中9-13步骤,是概述不通过xml文件,加载纹理图的方式。
完整代码:
#include <osgViewer/Viewer>
#include <osgEarth/MapNode>
#include <osgEarth/Registry>
#include <osgEarth/LandCoverLayer>
#include <osgEarthSplat/SplatLayer>
#include <osgEarthSplat/GroundCoverLayer>
#include <osgEarthDrivers/gdal/GDALOptions>
#include <osgEarthUtil/ExampleResources>
#include <osgEarthUtil/EarthManipulator>
#include <osgEarthSymbology/BillboardSymbol>
#include <osgGA/StateSetManipulator>
#include <osgViewer/ViewerEventHandlers>
#define LC "[splat] "
using namespace osgEarth;
using namespace osgEarth::Util;
using namespace osgEarth::Splat;
using namespace osgEarth::Drivers;
using namespace osgEarth::Symbology;
int
failed(const std::string& s) {
OE_WARN << "FAILED: " << s << "\n";
return -1;
}
int
main(int argc, char** argv)
{
osg::ArgumentParser arguments(&argc,argv);
bool fromXML = arguments.find("--xml") >= 0;
// Create a land cover dictionary.创建 陆地覆盖词典 对象
LandCoverDictionary* dictionary;
if (fromXML)
{
LandCoverDictionaryOptions options;
if (options.loadFromXML("D:/FreeXGIS/osgearth_gch/data/land_cover_dictionary.xml") == false)
return failed("Cannot find XML land cover dictionary");
dictionary = new LandCoverDictionary(options);
}
else
{
// 如果没有找到,则新建一个对象。下面的内容同xml中标签一致
dictionary = new LandCoverDictionary();
dictionary->setName("Land Cover Dictionary");
dictionary->addClass("forest");
dictionary->addClass("cropland");
dictionary->addClass("grassland");
dictionary->addClass("savanna");
dictionary->addClass("swamp");
dictionary->addClass("desert");
dictionary->addClass("rock");
dictionary->addClass("water");
dictionary->addClass("tundra");
dictionary->addClass("urban");
}
// Create the data source for our land cover data and
// map each value to a class in the dictionary.
// This example uses the ESA GLOBCOVER data set from
// http://due.esrin.esa.int/page_globcover.php
GDALOptions coverageDriver;// gdal驱动
//coverageDriver.url() = "D:/FreeXGIS/osgearth_gch/data/splat/GLOBCOVER_L4_200901_200912_V2.3_Ant_tiled.tif"; // 未找到,换一个
coverageDriver.url() = "earth_image/heightfield/30m.tif";// 30m高程数据
//coverageDriver.url() = "earth_image/globe/globel.tif";// 加载影像,拉进的过程中,控制台会输出错误信息
coverageDriver.profile() = ProfileOptions("global-geodetic");// 创建地球
// landCover 可序列化配置选项
LandCoverCoverageLayerOptions coverage;
coverage.driver() = coverageDriver;// 指定驱动
coverage.warp() = 0.035;// 弯曲率?
if (fromXML)
{
if (coverage.loadMappingsFromXML("D:/FreeXGIS/osgearth_gch/data/land_cover_ESA_GLOBCOVER.xml") == false)
return failed("Cannot find coverage mappings XML\n");
}
else
{
// 如果读取不到xml数据则创建。以下name值与dictionary的class值保持一致
coverage.map(11, "cropland");// 农田
coverage.map(14, "cropland");
coverage.map(20, "cropland");
coverage.map(30, "cropland");
coverage.map(40, "forest");// 森林
coverage.map(50, "forest");
coverage.map(60, "forest");
coverage.map(70, "forest");
coverage.map(80, "forest");
coverage.map(90, "forest");
coverage.map(100, "forest");
coverage.map(110, "grassland");// 草场
coverage.map(120, "grassland");
coverage.map(130, "savanna");// 稀树草原
coverage.map(140, "savanna");
coverage.map(150, "savanna");
coverage.map(160, "swamp");// 沼泽
coverage.map(170, "swamp");
coverage.map(180, "swamp");
coverage.map(190, "urban");// 城市
coverage.map(200, "desert");// 沙漠
coverage.map(210, "water");// 水
coverage.map(220, "tundra");// 冻土带
coverage.map(230, "water");
}
// Create the land cover layer for the map:
// 为地图创建土地覆盖层:
LandCoverLayer* landCover = new LandCoverLayer();
landCover->setName("LandCover");
landCover->options().cachePolicy() = CachePolicy::NO_CACHE;
landCover->options().coverages().push_back(coverage);
landCover->options().maxDataLevel() = 15u;// 最大层级
// Next, load the definitions that map land cover classes to actual textures.
// 接下来,加载将土地覆盖类映射到实际纹理的定义。
Surface* surface = new Surface();
// 在catalog中,指明了 每一个class对应的图,比如forest对应森林图
SplatCatalog* catalog = SplatCatalog::read("D:/FreeXGIS/osgearth_gch/data/splat/splat_catalog.xml");
if (catalog == 0L)
return failed("Reading splat catalog");
// 将读取到的策略,应用到surface中
surface->setCatalog(catalog);
// The zone designates the geographic area over which to apply the surface.
// At least one zone is required and by default it covers the entire map.
// 区域指定要应用曲面的地理区域。
// 至少需要一个区域,默认情况下它覆盖整个地图。
Zone* splatZone = new Zone();//定义一个区域,将特定表面或土地覆盖层限制在一组地理边界内。
splatZone->setSurface(surface);
// Create an imagery splatting layer that uses the configured land cover.
// 使用配置的土地覆盖图 创建 图像splatting层。即将纹理贴在地形上
SplatLayer* splatLayer = new SplatLayer();
splatLayer->setName("Splat imagery");
splatLayer->options().cachePolicy() = CachePolicy::NO_CACHE;
splatLayer->setLandCoverDictionary(dictionary);
splatLayer->setLandCoverLayer(landCover);
splatLayer->zones().push_back(splatZone);
// Now, the trees:
// Load a tree image and make a billboard symbol from it:
osg::ref_ptr<osg::Image> tree = URI("D:/FreeXGIS/osgearth_gch/data/splat/pine2.png").getImage();
if (tree.valid() == false)
return failed("Loading tree image");
// 标牌
BillboardSymbol* treeSymbol = new BillboardSymbol();
treeSymbol->setImage(tree.get());
treeSymbol->width() = 12.0f;
treeSymbol->height() = 16.0f;
// Add this symbol to a "frest" biome.
// 将此符号添加到“新”生物群落中。
GroundCoverBiomeOptions forestBiome;
forestBiome.biomeClasses() = "forest";
forestBiome.symbols().push_back(treeSymbol);
// Assemble the ground cover coniguration:
// 组装覆盖地面的配置
GroundCoverOptions treeOptions;
treeOptions.biomes().push_back(forestBiome);
treeOptions.maxDistance() = 15000.0;
treeOptions.density() = 4.0;
treeOptions.fill() = 0.85;
treeOptions.brightness() = 2.0;// 亮度
treeOptions.contrast() = 1.0;// 对比度
GroundCover* trees = new GroundCover(treeOptions);//控制地面覆盖物外观的接口。
// 新增一个区域,绘制trees
Zone* treeZone = new Zone();
treeZone->setGroundCover(trees);
// Now, create a ground cover layer for some trees.
// 现在,为这些树创建地面覆盖层。
GroundCoverLayer* treeLayer = new GroundCoverLayer();
treeLayer->setName("Ground cover");
treeLayer->options().lod() = 13;
treeLayer->setLandCoverDictionary(dictionary);
treeLayer->setLandCoverLayer(landCover);
treeLayer->zones().push_back(treeZone);
// Assemble the Map.组装地图
Map* map = new Map();
map->addLayer(dictionary);
map->addLayer(landCover);
map->addLayer(splatLayer);
map->addLayer(treeLayer);
// Activate the REX terrain engine (required for splatting)激活REX地形引擎
osgEarth::Registry::instance()->overrideTerrainEngineDriverName() = "rex";
// create a viewer:
osgViewer::Viewer viewer(arguments);
viewer.getDatabasePager()->setUnrefImageDataAfterApplyPolicy( false, false );
viewer.setCameraManipulator( new EarthManipulator(arguments) );
viewer.setSceneData(new MapNode(map));
viewer.addEventHandler(new osgViewer::StatsHandler());
viewer.addEventHandler(new osgViewer::WindowSizeHandler());
return viewer.run();
}