OSG和osgearth显示中文(转载)

OSG和osgearth显示中文(转载),第1张

OSG和osgearth显示中文(转载)

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;

修改效果如下:

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/588375.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-12
下一篇 2022-04-12

发表评论

登录后才能评论

评论列表(0条)

保存