osgearth加载mapbox在线高程数据

加载mapbox在线高程数据

mapbox的高程数据数据是rgba四通道的png格式栅格图像数据,而一般的高程数据HeightField是单通道的。

因此,在请求到源数据之后只需要做一次osg::Image -> osg::HeightField 的转换即可。
官网提供的转换公式:

height = -10000 + ((R * 256 * 256 + G * 256 + B) * 0.1)

通过继承 TileSource 重写 createImage 和 createHeightField 即可实现功能。

实现效果
下图为我国台湾省花莲机场傍山效果图。
在这里插入图片描述

头文件

重新定义配置选项

#include <osgEarth/Common>
#include <osgEarth/TileSource>
#include <osgEarth/URI>

using namespace osgEarth;

class CgMapboxTerrainOptions : public TileSourceOptions
{	
public:
	optional<URI>& url() { return _url; }
	const optional<URI>& url() const { return _url; }

	optional<bool>& invertY() { return _invertY; }
	const optional<bool>& invertY() const { return _invertY; }

	optional<std::string>& format() { return _format; }
	const optional<std::string>& format() const { return _format; }

public:
	CgMapboxTerrainOptions(const TileSourceOptions& opt = TileSourceOptions()) : TileSourceOptions(opt)
	{
		setDriver("mapboxTer");
		fromConfig(_conf);
	}

	CgMapboxTerrainOptions(const std::string& inUrl) : TileSourceOptions()
	{
		setDriver("mapboxTer");
		fromConfig(_conf);
		url() = inUrl;
	}

	virtual ~CgMapboxTerrainOptions() { }

public:
	Config getConfig() const {
		Config conf = TileSourceOptions::getConfig();
		conf.updateIfSet("url", _url);
		conf.updateIfSet("format", _format);
		conf.updateIfSet("invert_y", _invertY);
		return conf;
	}

protected:
	void mergeConfig(const Config& conf) {
		TileSourceOptions::mergeConfig(conf);
		fromConfig(conf);
	}

private:
	void fromConfig(const Config& conf) {
		conf.getIfSet("url", _url);
		conf.getIfSet("format", _format);
		conf.getIfSet("invert_y", _invertY);
	}

	optional<URI>         _url;
	optional<std::string> _format;
	optional<bool>        _invertY;
};

TileSource 的实现

通过继承 TileSource 重写 createImage 和 createHeightField,并自定义 TileSourceDriver 使用新的TileSource。

#include "CgMapboxTerrainOptions.h"

#include <osgEarth/ImageUtils>
#include <osgDB/FileNameUtils>
#include <osgDB/FileUtils>
#include <osgDB/Registry>
#include <osgDB/ReadFile>
#include <osgEarth/Registry>

class MapBoxTerrainSource : public TileSource
{
public:
	MapBoxTerrainSource(const TileSourceOptions& options)
		:TileSource(options), _options(options), _rotate_iter(0u)
	{
		
	}
	
	Status initialize(const osgDB::Options* dbOptions)
	{
		_dbOptions = Registry::instance()->cloneOrCreateOptions(dbOptions);

		URI xyzURI = _options.url().value();
		if (xyzURI.empty())
		{
			return Status::Error("Fail: driver requires a valid \"url\" property");
		}

		// driver requires a profile.
		if (!getProfile())
		{
			return Status::Error("An explicit profile definition is required by the XYZ driver.");
		}

		_template = xyzURI.full();

		_rotateStart = _template.find("[");
		_rotateEnd = _template.find("]");
		if (_rotateStart != std::string::npos && _rotateEnd != std::string::npos && _rotateEnd - _rotateStart > 1)
		{
			_rotateString = _template.substr(_rotateStart, _rotateEnd - _rotateStart + 1);
			_rotateChoices = _template.substr(_rotateStart + 1, _rotateEnd - _rotateStart - 1);
		}

		_format = _options.format().isSet()
			? *_options.format()
			: osgDB::getLowerCaseFileExtension(xyzURI.base());

		return STATUS_OK;
	}
	
	osg::Image* createImage(const TileKey& key, ProgressCallback*  progress)
	{
		unsigned x, y;
		key.getTileXY(x, y);

		if (_options.invertY() == true)
		{
			unsigned cols = 0, rows = 0;
			key.getProfile()->getNumTiles(key.getLevelOfDetail(), cols, rows);
			y = rows - y - 1;
		}

		std::string location = _template;

		// support OpenLayers template style:
		replaceIn(location, "${x}", Stringify() << x);
		replaceIn(location, "${y}", Stringify() << y);
		replaceIn(location, "${z}", Stringify() << key.getLevelOfDetail());

		// failing that, legacy osgearth style:
		replaceIn(location, "{x}", Stringify() << x);
		replaceIn(location, "{y}", Stringify() << y);
		replaceIn(location, "{z}", Stringify() << key.getLevelOfDetail());

		std::string cacheKey;

		if (!_rotateChoices.empty())
		{
			cacheKey = location;
			unsigned index = (++_rotate_iter) % _rotateChoices.size();
			replaceIn(location, _rotateString, Stringify() << _rotateChoices[index]);
		}


		URI uri(location, _options.url()->context());
		if (!cacheKey.empty())
			uri.setCacheKey(cacheKey);

		return uri.getImage(_dbOptions.get(), progress);
	}
	
	virtual std::string getExtension() const
	{
		return _format;
	}

	osg::HeightField* createHeightField(const TileKey& key, ProgressCallback* progress);
	
private:
	const CgMapboxTerrainOptions _options;
	std::string            _format;
	std::string            _template;
	std::string            _rotateChoices;
	std::string            _rotateString;
	std::string::size_type _rotateStart, _rotateEnd;
	OpenThreads::Atomic    _rotate_iter;

	osg::ref_ptr<osgDB::Options> _dbOptions;
};

osg::HeightField* MapBoxTerrainSource::createHeightField(const TileKey& key, ProgressCallback* progress)
{
	// height = -10000 + ((R * 256 * 256 + G * 256 + B) * 0.1)

	// MapBox encoded elevation PNG.
		// https://www.mapbox.com/blog/terrain-rgb/
	if (1/*_options.elevationEncoding().value() == "mapbox"*/)
	{
		if (getStatus().isError())
			return 0L;

		osg::HeightField *hf = 0;
		osg::ref_ptr<osg::Image> image = createImage(key, progress);
		if (image.valid())
		{
			// Allocate the heightfield.
			hf = new osg::HeightField();
			hf->allocate(image->s(), image->t());

			ImageUtils::PixelReader reader(image.get());
			for (unsigned int c = 0; c < image->s(); c++)
			{
				for (unsigned int r = 0; r < image->t(); r++)
				{
					osg::Vec4 pixel = reader(c, r);
					pixel.r() *= 255.0;
					pixel.g() *= 255.0;
					pixel.b() *= 255.0;
					float h = -10000.0f + ((pixel.r() * 65536.0f + pixel.g() * 256.0f + pixel.b()) * 0.1f);
					hf->setHeight(c, r, h);
				}
			}
		}
		return hf;
	}
	else
	{
		return TileSource::createHeightField(key, progress);
	}
}

class MapBoxTerrainTileSourceDriver : public TileSourceDriver
{
public:
	MapBoxTerrainTileSourceDriver()
	{
		supportsExtension("osgearth_mapboxTer", "mapboxTer Driver");
	}

	virtual const char* className()
	{
		return "mapboxTer Driver";
	}

	virtual ReadResult readObject(const std::string& file_name, const Options* options) const
	{
		if (!acceptsExtension(osgDB::getLowerCaseFileExtension(file_name)))
		{
			return ReadResult::FILE_NOT_HANDLED;
		}

		return new MapBoxTerrainSource(getTileSourceOptions(options));
	}
};

REGISTER_OSGPLUGIN(osgearth_mapboxTer, MapBoxTerrainTileSourceDriver)

使用

	CgMapboxTerrainOptions mbTerOpt;
	mbTerOpt.url() = "http://api.mapbox.com/v4/mapbox.terrain-rgb/{z}/{x}/{y}.pngraw?access_token=...";
	mbTerOpt.profile()->namedProfile() = "spherical-mercator";
	ElevationLayerOptions eoptions("mapboxEle", mbTerOpt);
	osg::ref_ptr<osgEarth::ElevationLayer> rpEleLayer = new osgEarth::ElevationLayer(eoptions);

	osg::ref_ptr<osgEarth::Map> rpMap = new osgEarth::Map(mapOpts);
	rpMap->addImageLayer(rpImagLayer.get());
	rpMap->addElevationLayer(rpEleLayer.get());
	

注意:access_token可在mapbox官网自行申请。

参考

本人在参考大佬的文章后自行实践,未能按照大佬的代码实现相关效果,一部分的原因是大佬给出的代码并不全。

在重新定义配置选项 CgMapboxTerrainOptions 和 TileSourceDriver 后,最终将mapbox在线高程数据加载出来了。

参考文章
osgearth加载mapbox在线高程数据

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值