栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > .Net

.NET6使用GDAL.Core中文乱码问题及解决方法

.Net 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

.NET6使用GDAL.Core中文乱码问题及解决方法

.NET6使用GDAL.Core中文乱码问题及解决方法
  • 1.前言
  • 2.问题类型及解决方案
    • 2.1分析
    • 2.2 问题类型及解决方案
  • 3.总结

1.前言

GDAL是一个处理矢量和栅格的GIS库,本文总结了在.NET6项目下使用Gdal.Core遇到的一些编码问题以及解决方案。
关键字:.NET6、Gdal.Core(版本2.3.0-beta-023)、中文乱码

2.问题类型及解决方案

首先创建一个.NET6项目后,安装Gdal.Core2.3.0-beta-023包(包含Gdal.Core与Gdal.Core.WindowsRuntime),注册GDAL和OGR组件后,开始对SHP进行操作,读写SHP时可能会遇到中文乱码问题。

2.1分析

中文显示乱码,归因还是编码问题,主要是由于GDAL与C#代码编码不一致,或者是封装层的编码不一致导致的。根据网上查的资料以及自己测试得出,GDAL中的中文编码有时候采用GBK,比如Ogr.Open(string utf8_path, int update)需要传入UTF8路径;

        //Ogr.Open(string utf8_path, int update)
        public static DataSource Open(string utf8_path, int update)
        {
            IntPtr intPtr = OgrPINVOKE.Open(StringToUtf8Bytes(utf8_path), update);
            DataSource result = (intPtr == IntPtr.Zero) ? null : new DataSource(intPtr, cMemoryOwn: true, ThisOwn_true());
            if (OgrPINVOKE.SWIGPendingException.Pending)
            {
                throw OgrPINVOKE.SWIGPendingException.Retrieve();
            }

            return result;
        }
        
        //Ogr.StringToUtf8Bytes(string str)
        internal static byte[] StringToUtf8Bytes(string str)
        {
            if (str == null)
            {
                return null;
            }

            byte[] array = new byte[Encoding.UTF8.GetMaxByteCount(str.Length) + 1];
            Encoding.UTF8.GetBytes(str, 0, str.Length, array, 0);
            return array;
        }

有时候采用GBK,比如属性字段名和属性值。而创建的文件中中文属性乱码,则是由于默认方法缺少.cpg文件(里面存有编码名称,ArcGIS根据该文件设置编码方式)。

2.2 问题类型及解决方案
  1. 无法打开中文路径 ,打开中文路径的shp会报错;
    网上针对无法打开中文路径,大多说法是修改配置"GDAL_FILENAME_IS_UTF8"为“YES”或"NO",这个可以根据当前编码是否为UTF8进行判断,在调用Ogr.RegisterAll();后使用。
        /// 
        /// 配置编码
        /// 
        private static void ConfigEncoding()
        {
            // 为了支持中文路径,如果默认编码非UTF8,请添加下面这句代码
            if (Encoding.Default.EncodingName != Encoding.UTF8.EncodingName || Encoding.Default.CodePage != Encoding.UTF8.CodePage)
            {
                var filenameConfig = Gdal.GetConfigOption("GDAL_FILENAME_IS_UTF8", string.Empty);
                if (filenameConfig!= "NO")
                {
                    Gdal.SetConfigOption("GDAL_FILENAME_IS_UTF8", "NO");
                }
            }
            try
            {
                Encoding gbk = Encoding.GetEncoding(FeatureExtensions.GdalEncoding);
            }
            catch (Exception e)//如果无法获取GBK编码,则需注册编码
            {
                Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
            }
        }
  1. 获取中文名称乱码,获取DataSource数据源或图层Layer的中文名称乱码;
    同样有网友说修改配置"SHAPE_ENCODING"为"",但本人试过多次依然乱码,因此可以通过导入GDAL函数,自行转码的方式进行处理,本文提供几个类以供参考。
    LayerExtensions
using OSGeo.OGR;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;

namespace EM.GIS.GdalExtensions
{
    /// 
    /// 图层扩展方法
    /// 
    public static class LayerExtensions
    {
        /// 
        /// 获取名称
        /// 
        /// 图层
        /// 名称
        [DllImport(FeatureExtensions.GdalDllName, CallingConvention = CallingConvention.Cdecl)]
        public extern static IntPtr OGR_L_GetName(HandleRef layer);
               /// 
        /// 获取名称
        /// 
        /// 图层
        /// 名称
        public static string GetNameUTF8(this Layer layer)
        {
            var layerRef = Layer.getCPtr(layer); 
             IntPtr strPtr = OGR_L_GetName(layerRef);
            string value = strPtr.IntPtrTostring(Encoding.UTF8);
            return value;
        }
    }
}

IntPtrExtensions

using System;
using System.Runtime.InteropServices;
using System.Text;

namespace EM.GIS.GdalExtensions
{
    /// 
    /// IntPtr扩展
    /// 
    public static class IntPtrExtensions
    {
        /// 
        /// 计算指定地址字节长度
        /// 
        /// 地址
        /// 字节长度
        public static int GetIntPtrLength(this IntPtr strPtr)
        {
            int size;
            for (size = 0; Marshal.ReadByte(strPtr, size) > 0; size++) ;
            return size;
        }
        /// 
        /// 从指定地址根据编码读取字符串
        /// 
        /// 地址
        /// 编码名称
        /// 字符串
        public static string IntPtrTostring(this IntPtr strPtr, string encodingName)
        {
            var encoding= Encoding.GetEncoding(encodingName);
            string value = strPtr.IntPtrTostring(encoding);
            return value;
        }
        /// 
        /// 从指定地址根据编码读取字符串
        /// 
        /// 地址
        /// 编码名称
        /// 字符串
        public static string IntPtrTostring(this IntPtr strPtr, Encoding encoding)
        {
            int size = GetIntPtrLength(strPtr);
            byte[] array = new byte[size];
            Marshal.Copy(strPtr, array, 0, size);
            string value = encoding.GetString(array);
            return value;
        }
        /// 
        /// 将字符串转成IntPtr
        /// 
        /// 字符串
        /// 编码
        /// IntPtr
        public static IntPtr StringToIntPtr(this string str, Encoding encoding)
        {
            byte[] array = encoding.GetBytes(str);
            GCHandle hObject = GCHandle.Alloc(array, GCHandleType.Pinned);
            IntPtr pObject = hObject.AddrOfPinnedObject();
            if (hObject.IsAllocated)
                hObject.Free();
            return pObject;
        }
    }
}
  1. 获取中文属性乱码,获取中文属性字段或者属性值出现乱码;
    与中文名称乱码处理方式一致,代码如下:
    FeatureExtensions
using OSGeo.OGR;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;

namespace EM.GIS.GdalExtensions
{
    /// 
    /// 要素扩展
    /// 
    public static class FeatureExtensions
    {
        /// 
        /// gdal文件名
        /// 
        public const string GdalDllName = "gdal202.dll";
        /// 
        /// gdal编码名称
        /// 
        public const string GdalEncoding = "GBK";

        /// 
        /// 获取要素指定字段的字符串值
        /// 
        /// 要素句柄
        /// 字段索引
        /// 字符串值
        [DllImport(GdalDllName, CallingConvention = CallingConvention.Cdecl)]
        public extern static IntPtr OGR_F_GetFieldAsString(HandleRef featureHandle, int fieldIndex);

        /// 
        /// 设置要素指定字段的值为字符串
        /// 
        /// 要素句柄
        /// 字段索引
        /// 值
        [DllImport(GdalDllName, CallingConvention = CallingConvention.Cdecl)]
        public extern static void OGR_F_SetFieldString(HandleRef featureHandle, int fieldIndex, IntPtr value);
        /// 
        /// 获取要素指定字段的字符串值
        /// 
        /// 要素
        /// 字段索引
        /// 字符串值
        public static string GetFieldAsStringUTF8(this Feature feature, int fieldIndex)
        {
            HandleRef handle = Feature.getCPtr(feature);
            IntPtr intptr = OGR_F_GetFieldAsString(handle, fieldIndex);
            string value = intptr.IntPtrTostring(Encoding.UTF8);
            return value;
        }
    }
}

FieldDefnExtensions

using OSGeo.OGR;
using System;
using System.Runtime.InteropServices;
using System.Text;

namespace EM.GIS.GdalExtensions
{
    public static class FieldDefnExtensions
    {
        /// 
        /// 获取字段名称
        /// 
        /// 字段定义
        /// 字段名称
        [DllImport(FeatureExtensions.GdalDllName, CallingConvention = CallingConvention.Cdecl)]
        public extern static IntPtr OGR_Fld_GetNameRef(IntPtr fieldDefn);
        /// 
        /// 获取字段名称
        /// 
        /// 字段定义
        /// 字段名称
        [DllImport(FeatureExtensions.GdalDllName, CallingConvention = CallingConvention.Cdecl)]
        public extern static IntPtr OGR_Fld_GetNameRef(HandleRef fieldDefn);
    
        /// 
        /// 获取字段名称
        /// 
        /// 字段定义
        /// 字段名
        public static string GetNameUTF8(this FieldDefn fieldDefn)
        {
            var fieldDefnRef = FieldDefn.getCPtr(fieldDefn);
            IntPtr strPtr = OGR_Fld_GetNameRef(fieldDefnRef);
            string value = strPtr.IntPtrTostring(Encoding.UTF8);
            return value;
        }
    }
}
  1. 创建带中文属性乱码,当创建带有中文属性字段或属性值时,在ArcGIS或QGIS中显示乱码;
    需要在创建时,增加配置"ENCODING=UTF-8",代码如下:
    DriverExtensions
using OSGeo.OGR;
using System;
using System.Linq;

namespace EM.GIS.GdalExtensions
{
    public static class DriverExtensions
    {
        /// 
        /// 复制数据源(解决写入中文乱码)
        /// 
        /// 驱动
        /// 原始数据源
        /// 目录
        /// 可选项
        /// 新的数据源
        public static DataSource CopyDataSourceUTF8(this Driver driver, DataSource srcDataSource, string path, string[] options)
        {
            DataSource destDataSource = null;
            if (driver!=null&&srcDataSource!=null&&!string.IsNullOrEmpty(path))
            {
                string[] destOptions = GetOptionsWithUTF8(options);
                if (srcDataSource==null)
                {
                    destDataSource=driver.CreateDataSource(path, destOptions);
                }
                else
                {
                    destDataSource=driver.CopyDataSource(srcDataSource, path, destOptions);
                }
            }
            return destDataSource;
        }
        /// 
        /// 获取包含UTF8编码的配置
        /// 
        /// 原有配置
        /// 新的配置
        public static string[] GetOptionsWithUTF8(this string[] options)
        {
            string encodingStr = "ENCODING=UTF-8";//配置增加编码,添加.cpg文件,以解决写入中文乱码
            string[] destOptions = options;
            if (destOptions==null)
            {
                destOptions=new string[] { encodingStr };
            }
            else
            {
                if (!destOptions.Contains(encodingStr))
                {
                    destOptions=new string[options.Length+1];
                    Array.Copy(options, destOptions, options.Length);
                    destOptions[destOptions.Length-1]=encodingStr;
                }
            }
            return destOptions;
        }
        /// 
        /// 创建数据源(解决写入中文乱码)
        /// 
        /// 驱动
        /// 目录
        /// 可选项
        /// 新的数据源
        public static DataSource CreateDataSourceUTF8(this Driver driver, string path, string[] options)
        {
            DataSource destDataSource = null;
            if (driver!=null&&!string.IsNullOrEmpty(path))
            {
                var destOptions = GetOptionsWithUTF8(options);
                destDataSource=driver.CreateDataSource(path, destOptions);
            }
            return destDataSource;
        }
    }
}
3.总结

本文通过调用导入函数再转码处理的方式解决中文乱码的问题,而如果C++能力较好的话可以自行修改GDAL源码中涉及编码部分代码,重新编译。另外本文中扩展类可在EMap代码仓库中获取。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/910866.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号