第10章 - 实用工具类详解

第10章 - 实用工具类详解

10.1 工具类概述

OGU4Net提供了一系列实用工具类:

工具类 命名空间 功能
EncodingUtil Utils 编码检测与转换
ZipUtil Utils ZIP压缩与解压
SortUtil Utils 自然排序
NumUtil Utils 数字格式化
ShpUtil Engine.Util Shapefile工具
PostgisUtil Engine.Util PostGIS工具
OgrUtil Engine.Util OGR辅助工具
GdalCmdUtil Engine.Util GDAL命令行工具

10.2 EncodingUtil编码工具

10.2.1 功能概述

EncodingUtil 提供文件编码检测和转换功能,特别适用于处理中文编码问题:

using OpenGIS.Utils.Utils;
using System.Text;

10.2.2 检测文件编码

// 检测文件编码
string filePath = "data.txt";
Encoding encoding = EncodingUtil.GetFileEncoding(filePath);
Console.WriteLine($"检测到编码: {encoding.WebName}");

// 使用检测到的编码读取文件
string content = File.ReadAllText(filePath, encoding);

10.2.3 检测流编码

using var stream = File.OpenRead("data.txt");
Encoding encoding = EncodingUtil.GetFileEncoding(stream);
Console.WriteLine($"编码: {encoding.WebName}");

// 注意:此方法会重置流位置

10.2.4 检测字节数组编码

byte[] buffer = File.ReadAllBytes("data.txt");
Encoding encoding = EncodingUtil.DetectEncoding(buffer);
Console.WriteLine($"编码: {encoding.WebName}");

10.2.5 转换文件编码

// 将文件转换为UTF-8编码
EncodingUtil.ConvertFileEncoding("data.txt", Encoding.UTF8);

// 转换为GBK(需要先注册编码提供程序)
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
EncodingUtil.ConvertFileEncoding("data.txt", Encoding.GetEncoding("GBK"));

10.2.6 编码检测原理

/*
检测顺序:
1. BOM (Byte Order Mark) 检测
   - UTF-8 BOM: EF BB BF
   - UTF-16 LE: FF FE
   - UTF-16 BE: FE FF

2. UTF-8 模式检测
   - 检查多字节字符的有效性

3. GBK/GB2312 模式检测
   - 检查双字节字符范围

4. 默认返回 UTF-8
*/

10.2.7 实际应用

// 处理Shapefile的DBF文件编码
public string ReadDbfField(string dbfPath, string fieldName)
{
    // 检测DBF文件编码
    var encoding = EncodingUtil.GetFileEncoding(dbfPath);
    
    // 使用正确编码读取
    var layer = ShpUtil.ReadShapefile(
        Path.ChangeExtension(dbfPath, ".shp"), 
        encoding);
    
    // 获取字段值
    var feature = layer.Features.FirstOrDefault();
    return feature?.GetValue(fieldName)?.ToString() ?? "";
}

10.3 ZipUtil压缩工具

10.3.1 压缩文件夹

using OpenGIS.Utils.Utils;

// 压缩整个文件夹
ZipUtil.Zip("source_folder/", "output.zip");

// 指定编码压缩
ZipUtil.Zip("source_folder/", "output.zip", Encoding.UTF8);

10.3.2 解压文件

// 解压到指定目录
ZipUtil.Unzip("archive.zip", "output_folder/");

// 指定编码解压(处理中文文件名)
ZipUtil.Unzip("archive.zip", "output_folder/", Encoding.GetEncoding("GBK"));

10.3.3 压缩多个文件

// 压缩指定的多个文件
var files = new[]
{
    "file1.txt",
    "file2.shp",
    "file3.dbf"
};

ZipUtil.CompressFiles(files, "archive.zip");

10.3.4 Shapefile打包

// 将Shapefile的所有组成文件打包
public void ZipShapefile(string shpPath, string zipPath)
{
    var baseName = Path.GetFileNameWithoutExtension(shpPath);
    var dir = Path.GetDirectoryName(shpPath) ?? ".";
    
    var extensions = new[] { ".shp", ".shx", ".dbf", ".prj", ".cpg", ".sbn", ".sbx" };
    
    var files = extensions
        .Select(ext => Path.Combine(dir, baseName + ext))
        .Where(File.Exists)
        .ToList();
    
    ZipUtil.CompressFiles(files, zipPath);
    Console.WriteLine($"已打包 {files.Count} 个文件到 {zipPath}");
}

10.4 SortUtil排序工具

10.4.1 自然排序

标准字符串排序可能不符合预期:

标准排序: file1, file10, file2, file9
自然排序: file1, file2, file9, file10

10.4.2 使用自然排序

using OpenGIS.Utils.Utils;

// 文件名排序
var files = new[] { "file10.txt", "file2.txt", "file1.txt", "file9.txt" };

var sorted = SortUtil.NaturalSort(files, f => f);

foreach (var file in sorted)
{
    Console.WriteLine(file);
}
// 输出:
// file1.txt
// file2.txt
// file9.txt
// file10.txt

10.4.3 排序对象列表

// 对象列表按某属性自然排序
class FileInfo
{
    public string Name { get; set; }
    public long Size { get; set; }
}

var fileInfos = new List<FileInfo>
{
    new() { Name = "chapter10.md", Size = 1024 },
    new() { Name = "chapter2.md", Size = 512 },
    new() { Name = "chapter1.md", Size = 256 }
};

var sorted = SortUtil.NaturalSort(fileInfos, f => f.Name);

foreach (var file in sorted)
{
    Console.WriteLine(file.Name);
}
// 输出:
// chapter1.md
// chapter2.md
// chapter10.md

10.4.4 处理测量点号

// 国土测量点号排序
var pointNumbers = new[] { "J1", "J10", "J2", "J11", "J3" };
var sorted = SortUtil.NaturalSort(pointNumbers, p => p);

// 结果: J1, J2, J3, J10, J11

10.5 NumUtil数字工具

10.5.1 避免科学计数法

using OpenGIS.Utils.Utils;

// 小数可能显示为科学计数法
double small = 0.00000123;
Console.WriteLine(small.ToString());  // 输出: 1.23E-06

// 使用NumUtil避免科学计数法
string plain = NumUtil.GetPlainString(small);
Console.WriteLine(plain);  // 输出: 0.00000123

10.5.2 大数值处理

// 大数也可能显示为科学计数法
double large = 123456789012345.0;
Console.WriteLine(large.ToString());  // 可能: 1.23456789012345E+14

string plain = NumUtil.GetPlainString(large);
Console.WriteLine(plain);  // 输出完整数字

10.5.3 坐标输出

// 输出坐标时避免科学计数法
public string FormatCoordinate(double x, double y, double? z = null)
{
    var xStr = NumUtil.GetPlainString(x);
    var yStr = NumUtil.GetPlainString(y);
    
    if (z.HasValue)
    {
        var zStr = NumUtil.GetPlainString(z.Value);
        return $"POINT Z ({xStr} {yStr} {zStr})";
    }
    
    return $"POINT ({xStr} {yStr})";
}

10.6 ShpUtil Shapefile工具

10.6.1 读写Shapefile

using OpenGIS.Utils.Engine.Util;

// 读取Shapefile(自动处理编码)
var layer = ShpUtil.ReadShapefile("data.shp");

// 指定编码读取
var gbkLayer = ShpUtil.ReadShapefile("data.shp", Encoding.GetEncoding("GBK"));

// 写入Shapefile
ShpUtil.WriteShapefile(layer, "output.shp", Encoding.UTF8);

10.6.2 编码处理

// 获取Shapefile编码
Encoding encoding = ShpUtil.GetShapefileEncoding("data.shp");
Console.WriteLine($"Shapefile编码: {encoding.WebName}");

// 创建CPG文件
ShpUtil.CreateCpgFile("data.shp", Encoding.UTF8);

10.6.3 获取边界

// 获取Shapefile的边界范围
var envelope = ShpUtil.GetShapefileBounds("data.shp");

if (envelope != null)
{
    Console.WriteLine($"X范围: {envelope.MinX} - {envelope.MaxX}");
    Console.WriteLine($"Y范围: {envelope.MinY} - {envelope.MaxY}");
}

10.6.4 修复Shapefile

// 修复可能损坏的Shapefile
// 通过读取后重新写入来修复一些问题
ShpUtil.RepairShapefile("damaged.shp");

10.7 PostgisUtil PostGIS工具

10.7.1 连接字符串构建

using OpenGIS.Utils.Engine.Util;

// 构建PostGIS连接字符串
string connStr = PostgisUtil.BuildConnectionString(
    host: "localhost",
    database: "mydb",
    user: "postgres",
    password: "secret",
    port: 5432
);

Console.WriteLine(connStr);
// 输出: PG:host=localhost dbname=mydb user=postgres password=secret port=5432

10.7.2 连接测试

// 测试PostGIS连接
bool canConnect = PostgisUtil.TestConnection(connStr);
if (canConnect)
{
    Console.WriteLine("连接成功");
}
else
{
    Console.WriteLine("连接失败");
}

10.8 GdalCmdUtil GDAL命令工具

10.8.1 执行GDAL命令

using OpenGIS.Utils.Engine.Util;

// 获取ogr2ogr命令路径
string ogr2ogr = GdalCmdUtil.GetOgr2OgrPath();

// 执行ogr2ogr转换
GdalCmdUtil.ExecuteOgr2Ogr(
    inputPath: "input.shp",
    outputPath: "output.geojson",
    outputFormat: "GeoJSON"
);

10.8.2 坐标转换命令

// 使用ogr2ogr进行坐标转换
GdalCmdUtil.ReprojectLayer(
    inputPath: "wgs84.shp",
    outputPath: "cgcs2000.shp",
    targetSrs: "EPSG:4490"
);

10.9 综合应用示例

10.9.1 批量处理工具

public class BatchProcessor
{
    public void ProcessDirectory(string inputDir, string outputDir)
    {
        // 确保输出目录存在
        Directory.CreateDirectory(outputDir);
        
        // 获取所有Shapefile并自然排序
        var shpFiles = Directory.GetFiles(inputDir, "*.shp");
        var sorted = SortUtil.NaturalSort(shpFiles, f => Path.GetFileName(f));
        
        foreach (var shpFile in sorted)
        {
            try
            {
                // 检测编码
                var encoding = ShpUtil.GetShapefileEncoding(shpFile);
                Console.WriteLine($"处理: {Path.GetFileName(shpFile)} (编码: {encoding.WebName})");
                
                // 读取
                var layer = ShpUtil.ReadShapefile(shpFile, encoding);
                
                // 处理...
                ProcessLayer(layer);
                
                // 写入
                var outputPath = Path.Combine(
                    outputDir, 
                    Path.GetFileNameWithoutExtension(shpFile) + ".geojson");
                
                OguLayerUtil.WriteLayer(DataFormatType.GEOJSON, layer, outputPath);
            }
            catch (Exception ex)
            {
                Console.Error.WriteLine($"处理失败: {shpFile} - {ex.Message}");
            }
        }
    }
    
    private void ProcessLayer(OguLayer layer)
    {
        // 添加面积字段
        layer.AddField(new OguField
        {
            Name = "Area",
            DataType = FieldDataType.DOUBLE
        });
        
        foreach (var feature in layer.Features)
        {
            if (!string.IsNullOrEmpty(feature.Wkt))
            {
                var area = GeometryUtil.AreaWkt(feature.Wkt);
                feature.SetValue("Area", area);
            }
        }
    }
}

10.9.2 数据打包工具

public class DataPackager
{
    public void PackageProject(string projectDir, string outputZip)
    {
        var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
        Directory.CreateDirectory(tempDir);
        
        try
        {
            // 复制并处理Shapefile
            foreach (var shpFile in Directory.GetFiles(projectDir, "*.shp"))
            {
                // 检测编码
                var encoding = ShpUtil.GetShapefileEncoding(shpFile);
                
                // 复制相关文件
                var baseName = Path.GetFileNameWithoutExtension(shpFile);
                var extensions = new[] { ".shp", ".shx", ".dbf", ".prj" };
                
                foreach (var ext in extensions)
                {
                    var srcFile = Path.Combine(projectDir, baseName + ext);
                    if (File.Exists(srcFile))
                    {
                        File.Copy(srcFile, Path.Combine(tempDir, baseName + ext));
                    }
                }
                
                // 创建CPG文件确保编码正确
                ShpUtil.CreateCpgFile(Path.Combine(tempDir, baseName + ".shp"), encoding);
            }
            
            // 打包
            ZipUtil.Zip(tempDir, outputZip);
            Console.WriteLine($"项目已打包: {outputZip}");
        }
        finally
        {
            // 清理临时目录
            if (Directory.Exists(tempDir))
            {
                Directory.Delete(tempDir, true);
            }
        }
    }
}

10.9.3 坐标格式化工具

public class CoordinateFormatter
{
    public string FormatTxtLine(OguCoordinate coord, int zoneNumber)
    {
        // 使用NumUtil避免科学计数法
        var pointNumber = coord.PointNumber ?? "";
        var ringNumber = coord.RingNumber ?? "";
        var x = NumUtil.GetPlainString(coord.X);
        var y = NumUtil.GetPlainString(coord.Y);
        var z = coord.Z.HasValue ? NumUtil.GetPlainString(coord.Z.Value) : "";
        var remark = coord.Remark ?? "";
        
        return $"{pointNumber}\t{ringNumber}\t{x}\t{y}\t{z}\t{remark}";
    }
    
    public void ExportToTxt(OguLayer layer, string outputPath)
    {
        var lines = new List<string>();
        
        // 添加头部
        lines.Add("点号\t圈号\tX\tY\tZ\t备注");
        
        foreach (var feature in layer.Features)
        {
            if (string.IsNullOrEmpty(feature.Wkt)) continue;
            
            var coord = OguCoordinate.FromWkt(feature.Wkt);
            coord.PointNumber = feature.GetValue("点号")?.ToString();
            coord.RingNumber = feature.GetValue("圈号")?.ToString();
            coord.Remark = feature.GetValue("备注")?.ToString();
            
            lines.Add(FormatTxtLine(coord, 39));
        }
        
        // 使用UTF-8编码保存
        File.WriteAllLines(outputPath, lines, Encoding.UTF8);
    }
}

10.10 小结

本章介绍了OGU4Net的实用工具类:

  1. EncodingUtil:编码检测和转换,解决中文乱码问题
  2. ZipUtil:ZIP压缩和解压,支持中文文件名
  3. SortUtil:自然排序,正确处理文件名中的数字
  4. NumUtil:数字格式化,避免科学计数法
  5. ShpUtil:Shapefile专用工具,编码处理和修复
  6. PostgisUtil:PostGIS连接和操作工具
  7. GdalCmdUtil:GDAL命令行工具封装

这些工具类解决了GIS开发中的常见问题,提高了开发效率。

posted @ 2025-12-03 15:51  我才是银古  阅读(0)  评论(0)    收藏  举报