osgEarth支持中文过程详解
OSG和osgearth显示中文
一、知识储备
要想很好的理解和解决这个问题,首先要了解什么是多字节和宽字节。
说实话我之前也知道这两个字节到底有什么区别,只是简单查了一下资料。
这里引用了这篇博客,我感觉博主写的很有意思,通俗易懂,在这里先谢谢这位博主的奉献。
http://blog.163.com/baijianguo00@126/blog/static/1375326052011018101334714/
二、问题提出
在大致了解了什么是多字节和款字节之后,我们来看看具体的问题。
osg是老外开发的源码,没办法对中文支持很差,虽然这一点儿也不能影响osg带给我们的快感,我们在使用osg中肯定会或多或少要显示中文,但是你会发现你按显示英文那样就做显示的都是乱码。
比如我使用LableNode 加一个标记在地球上,如下
labelGroup->addChild( new PlaceNode(mapNode, GeoPoint(geoSRS, 117.5, 39.38), "北京" , pin));
labelGroup->addChild( new PlaceNode(mapNode, GeoPoint(geoSRS, -100.10, 40.60), "U.S.A" , flag));
这时发现北京显示的乱码,而“U.S.A”显示是正确的,还有我们在加载矢量的shp数据时,如果是地名标记数据,显示在地球上也是乱码,还用LableControl显示中文时也是一样的。
这里有两种成功的方法,
一、改源码;
二、转换字符,其实这两种方法本质都是在显示中文时转换字符。
改源码一劳永逸但是难度大有风险而且还要重新编译,鉴于此我还是推荐第二种方法-------在程序实时地转换字符。
三、解决方法
中文显示要用宽字节,这里提供几个转换函数。
函数一:
void unicodeToUTF8(const wstring &src, string& result)
{
int n = WideCharToMultiByte( CP_UTF8, 0, src.c_str(), -1, 0, 0, 0, 0 );
result.resize(n);
::WideCharToMultiByte( CP_UTF8, 0, src.c_str(), -1, (char*)result.c_str(), result.length(), 0, 0 );
}
函数二:
void gb2312ToUnicode(const string& src, wstring& result)
{
int n = MultiByteToWideChar( CP_ACP, 0, src.c_str(), -1, NULL, 0 );
result.resize(n);
::MultiByteToWideChar( CP_ACP, 0, src.c_str(), -1, (LPWSTR)result.c_str(), result.length());
}
当物在osg程序中显示汉字时,就如下调用上述两个函数即可,
void gb2312ToUtf8(const string& src, string& result)
{
wstring strWideChar;
gb2312ToUnicode(src, strWideChar);
unicodeToUTF8(strWideChar, result);
}
我们拿上面显示的北京标记的做例子,
Style pin;
pin.getOrCreate<IconSymbol>()->url()->setLiteral(m_PngFilepath);//指定标注图片路径
pin.getOrCreate<osgEarth::Symbology::TextSymbol>()->font()=m_FontFilepath;//指定中文字体路径
pin.getOrCreate<osgEarth::Symbology::TextSymbol>()->encoding() = osgEarth::Symbology::TextSymbol::ENCODING_UTF8;
pin.getOrCreate<TextSymbol>()->alignment() = TextSymbol::ALIGN_CENTER_CENTER;
pin.getOrCreate<TextSymbol>()->fill()->color() = Color::Red;
std::string _strName;
_strName = "北京";
std::string _strWideName;
gb2312ToUtf8(_strName,_strWideName);//这时的_strWideName就是宽字节用来显示就正确了
labelGroup->addChild( new PlaceNode(mapNode, GeoPoint(geoSRS, 117.5, 39.38), _strWideName , pin));
显示地名标记和LableControl中的中文都是这个方法,先转换在显示。
最近在osg官网上发现osgEarth升级到2.2版了,看了发布说明,增加了不少新功能,所以赶紧把源码下下来,发现确实多了不少新功能,最大的升级是在对Qt的支持、墨卡托坐标系的支持,还多了不少例子。
但是我添加中文标注的矢量数据时,标注出现乱码的情况,经过对源码的研究,对源码进行了修改,标注的代码在AnnotationUtils类的CreateTextDrawable函数中的t->setText(string)代码,将该代码修改如下:
Setlocale(LC_ALL,"chs");
int wSize = text.size()*2+2;
wchar_t* wtext=new wchar_t[wSize+1];
memset(wtext,0,wSize+1);
mbstowcs(wtext,text.c_str(),wSize);
t->setText(wtext);
delete[] wtext;
wtext=NULL ;
/*2012年 最近在osg官网上发现osgEarth升级到2.2版了,看了发布说明,增加了不少新功能,所以赶紧把源码下下来,发现确实多了不少新功能,最大的升级是在对Qt的支持、墨卡托坐标系的支持,还多了不少例子。
但是我添加中文标注的矢量数据时,标注出现乱码的情况,经过对源码的研究,对源码进行了修改,标注的代码在AnnotationUtils类的CreateTextDrawable函数中的t->setText(string)代码,将该代码修改如下: */ Setlocale(LC_ALL,"chs"); +; wchar_t* wtext=]; memset(wtext,,wSize+); mbstowcs(wtext,text.c_str(),wSize); t->setText(wtext); delete[] wtext; wtext=NULL ; /* LabelControl中也存在类似问题,解决方法和标注一样,修改的函数为LabelControl::calcSize函数中修改即可。
*/
//显示label的代码src\osgEarthUtil\Controls.cpp的524行,修改如下。
LabelControl::calcSize(const ControlContext& cx, osg::Vec2f& out_size) { if ( visible() == true ) { // we have to create the drawable during the layout pass so we can calculate its size. LabelText* t = new LabelText(); #if 1 // needs a special shader // todo: doesn't work. why? osg::Program* program = new osg::Program(); program->addShader( new osg::Shader( osg::Shader::VERTEX, s_controlVertexShader ) ); program->addShader( new osg::Shader( osg::Shader::FRAGMENT, s_labelControlFragmentShader ) ); t->getOrCreateStateSet()->setAttributeAndModes( program, osg::StateAttribute::ON ); #endif // chinese support.part1 begin 2011-11-14 setlocale( LC_ALL, "chs" ); ); wchar_t* wtext = ]; mbstowcs(wtext, _text.c_str(), requiredSize+); t->setText(wtext); delete [] wtext; wtext = NULL; // chinese support. part1 end 2011-11-14 // chinese support.part1 begin 2011-11-14 //t->setText( _text ); // chinese support. part1 end 2011-11-14 // yes, object coords. screen coords won't work becuase the bounding box will be wrong. t->setCharacterSizeMode( osgText::Text::OBJECT_COORDS ); t->setCharacterSize( _fontSize ); // always align to top. layout alignment gets calculated layer in Control::calcPos(). t->setAlignment( osgText::Text::LEFT_TOP ); t->setColor( foreColor().value() ); if ( _font.valid() ) t->setFont( _font.get() ); // chinese support.part1 begin 2011-11-14 setlocale( LC_ALL,"C" ); // chinese support. part1 end 2011-11-14 if ( haloColor().isSet() ) { t->setBackdropType( osgText::Text::OUTLINE ); t->setBackdropOffset( 0.03 ); t->setBackdropColor( haloColor().value() ); } osg::BoundingBox bbox = t->getTextBB(); if ( cx._viewContextID != ~0u ) { //the Text's autoTransformCache matrix puts some mojo on the bounding box osg::Matrix m = t->getATMatrix( cx._viewContextID ); _bmin = osg::Vec3( bbox.xMin(), bbox.yMin(), bbox.zMin() ) * m; _bmax = osg::Vec3( bbox.xMax(), bbox.yMax(), bbox.zMax() ) * m; } else { _bmin = osg::Vec3( bbox.xMin(), bbox.yMin(), bbox.zMin() ); _bmax = osg::Vec3( bbox.xMax(), bbox.yMax(), bbox.zMax() ); } _renderSize.set( (_bmax.x() - _bmin.x()) + padding().x(), (_bmax.y() - _bmin.y()) + padding().y() ); _drawable = t; out_size.set( margin().x() + _renderSize.x(), margin().y() + _renderSize.y() ); } else { out_size.,); } //_dirty = false; } //原因是读取字符串的时候就没有支持双字节的字节,如中文。
//osgEarth中设置显示中文的label CString mss = _T("中国"); std::string ss = osgDB::convertUTF16toUTF8(mss.AllocSysString()); // set up a style to use for labels: osgEarth::Annotation::Symbology::Style labelStyle; labelStyle.getOrCreate<TextSymbol>()->encoding() = TextSymbol::ENCODING_UTF8; labelStyle.getOrCreate<TextSymbol>()->font() = std::string("fonts/SimHei.ttf");
osgearth打开中文路径失败,若是驱动器为gdal时,大体是由于gdal无法打开中文文件所造成的,可进行一下设置:
用的是最新的GDAL1.9,GDAL中有一个函数CPLGetConfigOption( "GDAL_FILENAME_IS_UTF8", "YES" )判断,通过判断是否是UTF8的编码,而且指定的默认值还是UTF8编码,在含有中文路径的字符串大多数的编码应该是GBK的编码,这样,系统就将GBK的编码当做UTF8的编码来进行转换,结果就是汉字全部是乱码,导致的结果就是找不到文件,所以打不开。
解决方法:
不改变GDAL源代码,在自己调用GDALRegisterAll()和OGRAllRegiser()函数后,加上下面一句即可。
CPLSetConfigOption("GDAL_FILENAME_IS_UTF8","NO");
包含的头文件为:cpl_conv.h
osgEarth支持中文过程详解
为使osgEarth支持中文,需对osgEarthAnnotation源码做出改动,现将过程记录如下以供参考。
①参考PlaceNode的构造函数发现,源码中使用了Init函数,其中需要对添加注记的CreateTextDrawable函数进行重载,步骤如下:
在AnnotationUtils.h中声明重载函数:
static osg::Drawable* createTextDrawable( const std::wstring& text, //支持宽字符 const TextSymbol* symbol, const osg::Vec3& positionOffset ); 在AnnotationUtils.cpp中定义重载函数(直接复制原createTextDrawable函数,本文只展示修改部分): osg::Drawable* AnnotationUtils::createTextDrawable(const std::wstring& text, const TextSymbol* symbol, const osg::Vec3& positionOffset ) { .... t->setText( text.c_str()); .... }
②在对AnnotationUtils修改后需要对PlaceNode做出修改。
首先需要添加宽字符变量,以与之前字符变量区分,在PlaceNode.h中添加 std::wstring _wtext;
此外对涉及文本的函数都需要重载,需要重载的函数在PlaceNode.h声明如下:
PlaceNode( MapNode* mapNode, const GeoPoint& position, osg::Image* iconImage, const std::wstring& labelText, const Style& style =Style() ); PlaceNode( MapNode* mapNode, const GeoPoint& position, const std::wstring& labelText, const Style& style =Style() ); void initw(); 重载的函数在PlaceNode.cpp中定义如下(直接复制原函数,本文只展示修改部分): PlaceNode::PlaceNode(MapNode* mapNode, const GeoPoint& position, osg::Image* image, const std::wstring& text, const Style& style ) : OrthoNode( mapNode, position ), _image ( image ), _wtext ( text ), _style ( style ), _geode ( 0L ) { initw(); } PlaceNode::PlaceNode(MapNode* mapNode, const GeoPoint& position, const std::wstring& text, const Style& style ) : OrthoNode( mapNode, position ), _wtext ( text ), _style ( style ), _geode ( 0L ) { initw(); } void PlaceNode::initw() { .... if ( _wtext.empty() && _style.has() ) { _wtext =StringToWString(_style.get()->content()->eval_r()); } .... text = AnnotationUtils::createTextDrawable( _wtext, _style.get(), osg::Vec3( (offset.x() + (s / ), offset.y(), ) ); }
在重写initw函数时涉及string转wstring的问题,参考网上转换方法,虽然我也不知道如何实现转换功能,但是确实成功转换了,共享如下:
首先需要在PlaceNode.h中声明函数(当然也可以声明在别的头文件中,自己知道如何调用即可):
std::wstring StringToWString(const std::string& s);
由于该函数涉及系统函数,所以需要在PlaceNode.h中包含系统头文件:
#include <windows.h>
在PlaceNode.cpp中定义函数:
std::wstring PlaceNode::StringToWString(const std::string& s) { std::wstring wszStr; , s.c_str(), -, NULL, NULL ); wszStr.resize(nLength); LPWSTR lpwszStr = new wchar_t[nLength]; MultiByteToWideChar( CP_ACP, , s.c_str(), -, lpwszStr, nLength ); wszStr = lpwszStr; delete [] lpwszStr; return wszStr; }
③所有修改完成之后需要对osgEarthAnnotation进行重新编译,将新生成的lib以及dll文件放到对应位置,此外,由于对源码做出了改动,所以需要将修改后的源码放到include文件夹中。
④编写地标添加程序,需要注意的是一定要设置字体以及字体编码,具体如何实现添加地标不做详解。
osgEarth::Style style;
osgEarth::Symbology::TextSymbol *textStyle=style.getOrCreateSymbol();
textStyle->font()="simsun.ttc";
textStyle->size()=30.0;
textStyle->encoding()=osgEarth::Symbology::TextSymbol::ENCODING_UTF8;
修改效果如下:
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)