您运行的 Shapefiles 转换已经成功百次,但这次因 TransformationException 而中止。没有部分输出,也没有明确指示是哪条记录导致了问题。通常,罪魁祸首是一条埋在成千上万有效要素中的无效坐标。本文解释了为何 Shapefile 转换会因该错误而失败,以及如何在 C# 中使用 OperationErrorCollector(在 Aspose.GIS for .NET 26.6 中引入)进行修复。您将学习如何让转换完成,保留每个有效要素,并获取需要关注的记录的精确报告。

如果您只需要基本的转换代码,请参阅 Convert Shapefile to KML in C#。本指南在此基础上进行扩展,重点处理错误。

为什么 Shapefile 转换会抛出 TransformationException

大多数目标格式都要求使用特定的坐标系。例如,KML 总是使用 WGS 84 经度和纬度。在转换过程中,Aspose.GIS 会将源坐标系中的每个坐标转换为目标坐标系。如果任何坐标无法转换,库会抛出 TransformationException,并且转换会停止。

最常见的原因是:

  • 占位符 “no data” 值。 某些工具会写入哨兵值,而不是让几何对象为空。本文示例文件包含一个点,坐标为 (-1.7976931348623157E+308, -1.7976931348623157E+308),这是 double 的最小值,任何坐标系都无法转换它。
  • 超出范围的坐标。 这些值超出源坐标系的有效范围,通常是由于数据录入错误或单位换算错误导致的。
  • .prj 文件与数据不匹配。 如果以米为单位的投影坐标被声明为以度为单位的地理坐标,许多数值将远远超出有效范围。
  • 损坏的几何记录。 旧版导出和损坏的文件可能在单个记录中包含无效的数值。

在每种情况下,问题通常仅限于少数记录,但默认行为会丢弃整个转换。

为什么此功能重要

在第一次失败时停止是安全的,但在实际流水线中代价高昂。一个错误记录会迫使您在任何数据转换之前手动清理文件,而仅凭异常本身无法告诉您还有多少其他记录受到影响。启用错误收集后,您可以:

  • 将所有有效特征转换,而不是因单个错误记录导致整个文件丢失。
  • 记录每个被跳过特征的索引和坐标,以便修复源数据。
  • 在脏输入情况下运行无人值守的批量转换和 ETL 作业而不崩溃。
  • 接受 Web 服务中用户上传的 Shapefile,并将数据问题反馈给用户。

如何在 C# 中使用 Aspose.GIS 修复 Shapefile 转换失败

Aspose.GIS for .NET 是一个托管库,用于读取、写入和转换地理空间格式,例如 Shapefile、KML、GeoJSON、GML 和文件地理数据库,无需安装其他 GIS 软件。错误收集需要 26.6 或更高版本。请从 NuGet 安装该包:

dotnet add package Aspose.GIS

或者使用包管理器控制台:

Install-Package Aspose.GIS

本教程中使用以下类型:

  • VectorLayer (Aspose.Gis): 打开、创建和转换矢量图层。VectorLayer.Convert 执行转换。
  • ConversionOptions (Aspose.Gis): 保存转换设置,包括 DestinationDriverOptionsDestinationSpatialReferenceSystem
  • KmlOptions (Aspose.Gis.Formats.Kml): KML 驱动程序选项。它继承自 DriverOptionsErrorCollector 属性。
  • OperationErrorCollector (Aspose.Gis.Operations): 存储可恢复的错误。它公开 ErrorsCountHasErrorsAddClear
  • OperationErrorTransformationError (Aspose.Gis.Operations): 每个错误都有 MessageExceptionTransformationError 额外包含 FeatureIndexXYZ
  • TransformationException (Aspose.Gis.SpatialReferencing): 当坐标无法转换且未附加收集器时抛出。

如何修复 Shapefile 转换期间的 TransformationException

此修复分为两部分。首先,附加一个 OperationErrorCollector,使转换在遇到无效要素时跳过而不是失败。其次,使用收集的报告在源头修复或删除这些记录。下面的步骤以 Shapefile 转 KML 转换为例。

1. 准备环境

  1. 创建一个 .NET 控制台项目并添加 Aspose.GIS 26.6+ NuGet 包。
  2. 将 Shapefile 及其伴随文件(.shp.shx.dbf.prj)复制到同一个文件夹。本示例使用 data/light-traffics.shp
  3. 添加所需的命名空间:
using System;
using System.IO;
using Aspose.Gis;
using Aspose.Gis.Formats.Kml;
using Aspose.Gis.Operations;
using Aspose.Gis.SpatialReferencing;

2. 创建 OperationErrorCollector

收集器记录在转换过程中抛出的每个可恢复错误。为每次转换创建一个新实例,以免不同文件的错误混在一起。

// Records recoverable errors instead of throwing them.
var errors = new OperationErrorCollector();

3. 通过 ConversionOptions 附加收集器

将收集器分配给 KmlOptions.ErrorCollector,然后将 KML 选项作为 DestinationDriverOptions 传递。将 DestinationSpatialReferenceSystem 设置为 WGS 84 对于 KML 是可选的,但它可以在代码中明确目标坐标系。

var options = new ConversionOptions
{
    // KML always uses WGS 84; stating it makes the reprojection explicit.
    DestinationSpatialReferenceSystem = SpatialReferenceSystem.Wgs84,
    DestinationDriverOptions = new KmlOptions
    {
        ErrorCollector = errors // Skip and record features that fail transformation.
    }
};

4. 运行转换

调用 VectorLayer.Convert,使用源路径、Shapefile 驱动程序、目标路径、KML 驱动程序,以及您刚刚配置的选项。

string sourcePath = Path.Combine("data", "light-traffics.shp");
string destinationPath = Path.Combine("output", "light-traffics.kml");
Directory.CreateDirectory(Path.GetDirectoryName(destinationPath));

VectorLayer.Convert(sourcePath, Drivers.Shapefile, destinationPath, Drivers.Kml, options);

当某个要素无法转换时,KML 驱动程序会向收集器添加错误,跳过该要素并继续处理下一个要素。不会抛出 TransformationException

5. 报告已跳过的功能并验证输出

即使转换正常返回,仍可能跳过某些要素,因此始终在之后检查收集器。将每个错误强制转换为 TransformationError 以获取要素索引和失败的坐标,然后打开输出文件以确认写入了多少要素。

if (errors.HasErrors)
{
    Console.WriteLine($"Skipped {errors.Count} feature(s):");

foreach (var error in errors.Errors)
    {
        if (error is TransformationError transformationError)
        {
            Console.WriteLine(
                $"  Feature #{transformationError.FeatureIndex} at " +
                $"({transformationError.X}, {transformationError.Y}, {transformationError.Z}): {error.Message}");
        }
        else
        {
            Console.WriteLine($"  {error.Message}");
        }
    }
}

using (var layer = VectorLayer.Open(destinationPath, Drivers.Kml))
{
    Console.WriteLine($"Features written to KML: {layer.Count}");
}

对于示例文件,收集器记录了一个占位点错误,其余特征(至少 444 条)被写入 KML 文件。

此报告中的要素索引和坐标已完成修复。
在数据清理工作流中打开源 Shapefile,定位报告的记录,并对其进行纠正或删除。
如果许多记录在看似合理的值下仍然失败,请首先检查 .prj 文件,因为坐标系不匹配很可能是导致此问题的原因。

6. 完整示例代码

下面的完整控制台应用程序运行两次转换。第一次运行使用默认设置并重现 TransformationException。第二次运行附加 OperationErrorCollector,跳过无效特性,并打印报告。

using System;
using System.IO;
using Aspose.Gis;
using Aspose.Gis.Formats.Kml;
using Aspose.Gis.Operations;
using Aspose.Gis.SpatialReferencing;

namespace ShapefileTransformationErrors
{
    internal class Program
    {
        private static void Main()
        {
            string sourcePath = Path.Combine("data", "light-traffics.shp");
            string outputFolder = "output";
            Directory.CreateDirectory(outputFolder);

// -----------------------------------------------------------------
            // Run 1: default behavior. The first failed transformation aborts
            // the whole conversion with a TransformationException.
            // -----------------------------------------------------------------
            string failFastPath = Path.Combine(outputFolder, "fail-fast.kml");
            try
            {
                VectorLayer.Convert(sourcePath, Drivers.Shapefile, failFastPath, Drivers.Kml);
                Console.WriteLine("Default conversion finished without transformation errors.");
            }
            catch (TransformationException ex)
            {
                Console.WriteLine($"Default conversion aborted: {ex.Message}");
                Console.WriteLine($"Failing coordinate: ({ex.X}, {ex.Y}, {ex.Z})");
            }

// -----------------------------------------------------------------
            // Run 2: error-tolerant conversion. Invalid features are skipped
            // and recorded in the collector; valid features are written.
            // -----------------------------------------------------------------
            string destinationPath = Path.Combine(outputFolder, "light-traffics.kml");

var errors = new OperationErrorCollector();
            var options = new ConversionOptions
            {
                DestinationSpatialReferenceSystem = SpatialReferenceSystem.Wgs84,
                DestinationDriverOptions = new KmlOptions
                {
                    ErrorCollector = errors
                }
            };

VectorLayer.Convert(sourcePath, Drivers.Shapefile, destinationPath, Drivers.Kml, options);

// Report every skipped feature so the source data can be repaired.
            if (errors.HasErrors)
            {
                Console.WriteLine($"Conversion completed with {errors.Count} skipped feature(s):");

foreach (var error in errors.Errors)
                {
                    if (error is TransformationError transformationError)
                    {
                        Console.WriteLine(
                            $"  Feature #{transformationError.FeatureIndex} at " +
                            $"({transformationError.X}, {transformationError.Y}, {transformationError.Z}): {error.Message}");
                    }
                    else
                    {
                        Console.WriteLine($"  {error.Message}");
                    }
                }
            }
            else
            {
                Console.WriteLine("Conversion completed with no errors.");
            }

// Verify the output file.
            using (var layer = VectorLayer.Open(destinationPath, Drivers.Kml))
            {
                Console.WriteLine($"Features written to KML: {layer.Count}");
            }
        }
    }
}

7. 常见陷阱及如何避免

陷阱原因解决方案
ErrorCollectorOperationErrorCollector 无法编译两者均在 Aspose.GIS for .NET 26.6 中添加。将 NuGet 包升级到 26.6 或更高版本。
转换仍然抛出 TransformationException未将收集器附加到目标驱动程序选项。在分配给 ConversionOptions.DestinationDriverOptions 的驱动程序选项对象上设置 ErrorCollector
“成功”的转换缺少要素收集器抑制了异常,因此 Convert 正常返回。在每次调用后检查 errors.HasErrors 并记录结果。
将跳过的要素视为已修复收集器会跳过无效记录;它不会修复这些记录。使用报告的要素索引和坐标来纠正或删除源数据中的记录。
数百个要素一次性失败.prj 文件可能与实际坐标不匹配。在调查单个记录之前,先验证源坐标系。
多个文件的错误出现在同一报告中在多个转换中重复使用了同一个收集器实例。为每个文件创建新的 OperationErrorCollector,或在运行之间调用 Clear()
重复运行时出现文件锁错误使用 VectorLayer.Open 打开的图层未被释放。VectorLayer.Open 包装在 using 块中。

获取免费许可证

您可以从 Aspose 临时许可证页面获取 Aspose.GIS 的临时免费许可证:https://purchase.aspose.com/temporary-license/

免费附加资源

结论

A TransformationException 在 Shapefile 转换过程中通常表示少量记录包含无法转换的坐标,例如占位符值、超出范围的数字,或与其 .prj 文件不匹配的数据。 在 C# 中修复它需要两个步骤:首先附加 OperationErrorCollector,让 Aspose.GIS for .NET 跳过无效要素并完成转换;然后使用收集到的要素索引和坐标来修复源数据。 结果是一个管道,能够持续输出有效结果,同时将硬性失败转化为可操作的数据质量报告。

常见问题

  1. 为什么在转换 Shapefile 时会出现 TransformationException? 它发生在坐标无法从源坐标系转换到目标坐标系时。常见原因包括占位符 “no data” 值、坐标超出其坐标系的有效范围、.prj 文件与实际数据不匹配,以及几何记录损坏。

  2. 默认情况下,当坐标无法转换时会发生什么?
    VectorLayer.Convert 抛出 TransformationException 并且转换停止。自 26.6 版起,异常还会公开导致失败的坐标的 XYZ 值。

  3. 哪个版本的 Aspose.GIS for .NET 支持 OperationErrorCollector? OperationErrorCollectorDriverOptions.ErrorCollector 属性是在 Aspose.GIS for .NET 26.6 中引入的。早期版本不包含它们。

  4. OperationErrorCollector 是否修复无效坐标? 不。它会跳过转换失败的要素并记录它们,因此输出仅包含有效的要素。使用报告的要素索引和坐标来修复或删除源数据中的错误记录。

  5. 我可以在除 KML 之外的输出格式中使用 OperationErrorCollector 吗? ErrorCollector 在基类 DriverOptions 中定义,因此每个驱动程序选项类都公开它。文档示例涵盖 KML 和 MapInfo TAB 目标;在生产环境中依赖之前,请使用您自己的目标驱动程序测试其行为。

  6. 每个收集的错误包含哪些细节? 每个 OperationError 提供一个 Message 和底层的 Exception。转换失败会以 TransformationError 对象的形式报告,该对象会添加失败坐标的 FeatureIndex 以及 XYZ 值。

  7. 我如何知道转换是否在没有任何错误的情况下完成?VectorLayer.Convert 返回后,检查收集器的 HasErrorsCount 属性。即使转换未抛出异常,也可能跳过了一些要素。

阅读更多