采用ISO8211封装的S57数据,中文读取时乱码及丢字原因分析与解决方法

    很多GIS爱好者或ECDIS开发商在读取S57数据文件时多参考了“ISO8211lib is a C++ library for reading ISO8211-formatted files, such as SDTS and S-57 format “,S57数据NATF字段采用Unicode双字节编码国家属性字段,也就是说S57数据中只有NATF字段的解析与处理涉及了双字节数据问题,特别是NATF字段是可变长度字段,而在

ISO8211.lib

DDFSubfieldDefn::GetDataLength( const char * pachSourceData, int nMaxBytes, int * pnConsumedBytes )

在处理数据长度时最初只考虑了单字节定界符 UT = 31,FT = 30,而将双字节数据当成一种错误数据处理,参考

     /* We only check for the field terminator because of some buggy 
         * datasets with missing format terminators.  However, we have found
         * the field terminator is a legal character within the fields of
         * some extended datasets (such as JP34NC94.000).  So we don't check
         * for the field terminator if the field appears to be multi-byte
         * which we established by the first character being out of the 
         * ASCII printable range (32-127). 
         */

这样会造成测算出的字段长度不正确,因为双字节的字串中的个别字节很可能会出现与定界符冲突,造成数据长度错误,产生丢字问题!考虑到S57规定双字节单元定界符为(0/0) (1/15),字段定界符为(0/0)(1/14)  参见S57 3.10,实际让就是00,1F和00,1E,但在比较时,还要注意系统采用的是小尾序还是大尾序,我使用的系统环境是WINDOWS,大尾序,因此应对上述数据长度测量函数进行修改:

int DDFSubfieldDefn::GetDataLength( const char * pachSourceData,
                                    int nMaxBytes, int * pnConsumedBytes )


{
    if( !bIsVariable ) // 如果数据字段是定长字段
    {
        if( nFormatWidth > nMaxBytes )
        {
            CPLError( CE_Warning, CPLE_AppDefined, 
                      "Only %d bytes available for subfield %s with\n"
                      "format string %s ... returning shortened data.",
                      nMaxBytes, pszName, pszFormatString );


            if( pnConsumedBytes != NULL )
                *pnConsumedBytes = nMaxBytes;


            return nMaxBytes;
        }
        else
        {
            if( pnConsumedBytes != NULL )
                *pnConsumedBytes = nFormatWidth;


            return nFormatWidth;
        }
    }
    else   // 数据字段为变长字段
    {
        int     nLength = 0;
        int     bCheckFieldTerminator = TRUE;


        /* We only check for the field terminator because of some buggy 
         * datasets with missing format terminators.  However, we have found
         * the field terminator is a legal character within the fields of
         * some extended datasets (such as JP34NC94.000).  So we don't check
         * for the field terminator if the field appears to be multi-byte
         * which we established by the first character being out of the 
         * ASCII printable range (32-127). 
         */


        if( pachSourceData[0] < 32 || pachSourceData[0] >= 127 )    // 如果第一个字符为不可见字符,则认为数据为双字节字符集,不检查字段定界符

           bCheckFieldTerminator = FALSE;
        
        while( nLength < nMaxBytes
               && pachSourceData[nLength] != chFormatDelimeter )
        {
               if( bCheckFieldTerminator 
                         && pachSourceData[nLength] == DDF_FIELD_TERMINATOR )
                break;
            nLength++;
        }


        if( pnConsumedBytes != NULL )
        {
            if( nMaxBytes == 0 )
                *pnConsumedBytes = nLength;
            else
                *pnConsumedBytes = nLength+1;
        }
        
        return nLength;
    }

笔者认为可以这样解决(程序上文不变)

        if( pachSourceData[0] < 32 || pachSourceData[0] >= 127 ) 
           bCheckFieldTerminator = FALSE;
        
        while( nLength < nMaxBytes)
        {

               if( bCheckFieldTerminator)

{

if(pachSourceData[nLength] == chFormatDelimeter) 

break;

}

               else

{

if(pachSourceData[nLength] == chFormatDelimeter  && pachSourceData[nLength+1] == 0 )

 break;

}

if(pachSourceData[nLength] == DDF_FIELD_TERMINATOR && pachSourceData[nLength+1] ) break;
           nLength++;
       }


        if( pnConsumedBytes != NULL )
        {
            if( nMaxBytes == 0 )
                *pnConsumedBytes = nLength;
            else

              *pnConsumedBytes = nLength+1;

        }    

        return nLength;
    }

实践中很好的解决了问题

第二个问题,汉字乱码

在后面的数据处理时,由于中文操作系统的汉字一般采用GB18030,也就是GBK编码,因此,在显示这些汉字时还要将NATF字段转成GBK

可以直接使用系统的转换函数:

int len = WideCharToMultiByte(54936,0,(LPCWSTR )tmpStr,m_strLength/2,tmpStr1,m_strLength,0,0);

其中54936是GB18030的CodePage代码

其实第一个问题是在处理汉字乱码问题时发现的,原来只考虑了汉字编码转换问题,而且第二个问题解决后,丢字问题才在一个偶然的时候发现,这个问题在一些商用ECDIS上也存在。

以上浅见,请大师们指正。

 


  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是一个osgEarth 3.2中使用GDAL读取S57数据并将其作为MapNode添加到数地球中进行显示的代码示例: ```cpp #include <osgEarth/MapNode> #include <osgEarth/Registry> #include <osgEarthUtil/Controls> #include <osgEarthUtil/EarthManipulator> #include <osgDB/ReadFile> #include <gdal_priv.h> using namespace osgEarth; using namespace osgEarth::Util; using namespace osgEarth::Util::Controls; int main(int argc, char** argv) { // 初始化GDAL GDALAllRegister(); // 创建地球窗口 osgViewer::Viewer viewer; viewer.setSceneData(new osg::Group); // 创建地球 osgEarth::Map* map = new osgEarth::Map(); // 打开S57文件 GDALDataset* ds = (GDALDataset*)GDALOpen("path/to/s57_file", GA_ReadOnly); if (ds == NULL) { OSG_WARN << "Unable to open S57 file" << std::endl; return 1; } // 获取图层数量 int layerCount = ds->GetLayerCount(); // 循环遍历所有图层 for (int i = 0; i < layerCount; ++i) { OGRLayer* layer = ds->GetLayer(i); // 获取图层的空间参考信息 OGRSpatialReference* srs = layer->GetSpatialRef(); if (srs == NULL) { OSG_WARN << "Unable to get layer spatial reference" << std::endl; continue; } // 创建osgEarth中的SpatialReference对象 SpatialReference* osgSrs = SpatialReference::createFromHandle((void*)srs); // 创建osgEarth中的S57FeatureSource对象 S57FeatureOptions options; options.url() = "path/to/s57_file"; options.layerName() = layer->GetName(); options.spatialReference() = osgSrs; S57FeatureSource* featureSource = new S57FeatureSource(options); // 创建osgEarth中的FeatureModelLayer对象 FeatureModelLayerOptions fmlOptions; fmlOptions.name() = layer->GetName(); fmlOptions.featureSource() = featureSource; FeatureModelLayer* featureModelLayer = new FeatureModelLayer(fmlOptions); // 将FeatureModelLayer添加到地图中 map->addLayer(featureModelLayer); } // 创建MapNode对象 MapNode* mapNode = new MapNode(map); // 将MapNode添加到场景图中 viewer.getSceneData()->asGroup()->addChild(mapNode); // 启动OSG查看器 viewer.setCameraManipulator(new EarthManipulator); viewer.realize(); return viewer.run(); } ``` 需要注意的是,上述代码中使用了GDAL库来读取S57文件。在使用GDAL之前,需要在代码中调用`GDALAllRegister()`方法进行初始化。另外,由于S57文件包含的是矢量数据,因此需要将其转换为osgEarth中的FeatureModelLayer对象进行显示。上述代码中通过创建S57FeatureSource和FeatureModelLayer对象来完成这一操作。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值