NetTopologySuite 處理 GIS (2):WriteAllFeatures 型別轉換錯誤 Exception
在上一篇文章中,我們順利的使用 NetTopologySuite.IO.Esri 來讀取 Shapefile 檔案中的內容,可是在寫出時遇到了一點問題:

因此,這篇會探討的主題就是:
- 為甚麼會發生這個 Exception
- 如何解決
這個 Exception 哪裡來
一開始看到錯誤原因的時候真的覺得很茫然 😵 我很確定我的 WKT 是 Polygon 沒有錯,為甚麼 Exception 會告訴我我想把 Polygon 轉換成 Multi Polygon 呢 💫
於是開啟 debbug mode,順著程式碼一行一行往下找,終於在 Shapefile.cs 中找到一點線索。在最後要使用 Writer 寫入的時候,他所建立的 Writer 泛型所吃的類別竟然只有 Multi Polygon 的選項 😨

也就是說,Writer 以 Multi Polygon 建立,但我的 Geometry 型別是 Polygon,所以在使用 Writer 前他會一直想把我的型別轉換成 Multi Polygon ,才會一直噴出型別轉換的 Exception。但我仔細看了原始碼,居然只有針對 MultiPolygon 去進行寫入的方法,而且是把所有 Polygon 相關的類別:Polygon、PolygonM、PolygonZ、PolygonZM 都讓 Multi Polygon Writer 去接,好神祕的操作 😵
把 ShapefilePolygonWriter 獨立出來
查到原因之後我最直覺的想法就是把 Polygon Writer 的部分獨立出來,要修改的部分包含四個:ShapeType、Shapefile、ShapefileWriter 和 ShpWriter。
ShapeType.cs
在這邊新增一個判斷,讓 IsPolygon 只允許 Polygon 通過,其餘的類別都屬於 MultiPolygon。
/// <summary>
/// Indicates if shape associated with this type is a Polygon.
/// </summary>
public static bool IsPolygon(this ShapeType type)
{
// 修改成只符合 Polygon
return type == ShapeType.Polygon;
}
/// <summary>
/// Indicates if shape associated with this type is a Multi Polygon.
/// </summary>
public static bool IsMultiPolygon(this ShapeType type)
{
// 將其他 Polygon 類別歸到 MultiPolygon
return type == ShapeType.PolygonM
|| type == ShapeType.PolygonZM
|| type == ShapeType.PolygonZ;
}ShpPolygonWriter.cs
將原本的 ShpPolygonWriter.cs 複製一份,其中一份改名成 MultiPolygon,剩下的那一份調整成 Polygon 的型別。這個地方負責的是寫入檔案,因此要把原本的寫法轉換成適合 Polygon 的樣子。
/// <summary>
/// Polygon SHP file writer.
/// </summary>
public class ShpPolygonWriter : ShpWriter<Polygon>
{
/// <inheritdoc/>
public ShpPolygonWriter(Stream shpStream, Stream shxStream, ShapeType type) : base(shpStream, shxStream, type)
{
if (!ShapeType.IsPolygon())
ThrowUnsupportedShapeTypeException();
}
internal override void WriteGeometry(Polygon polygon, Stream stream)
{
var partsBuilder = new ShpMultiPartBuilder(1, 4);
partsBuilder.AddPart(polygon.Shell.CoordinateSequence);
partsBuilder.WriteParts(stream, HasZ, HasM);
partsBuilder.UpdateExtent(Extent);
}
}ShapefilePolygonWriter.cs
將原本的 ShapefilePolygonWriter.cs 複製一份出來,其中一份修改成 MultiPolygon,另外一份則調整成 Polygon 的型別。
/// <summary>
/// Polygon shapefile writer.
/// </summary>
public class ShapefilePolygonWriter : ShapefileWriter<Polygon> // 型別改成 Polygon
{
/// <inheritdoc/>
public ShapefilePolygonWriter(Stream shpStream, Stream shxStream, Stream dbfStream, ShapeType type, IReadOnlyList<DbfField> fields, Encoding encoding = null)
: base(shpStream, shxStream, dbfStream, type, fields, encoding)
{ }
/// <inheritdoc/>
public ShapefilePolygonWriter(string shpPath, ShapeType type, IReadOnlyList<DbfField> fields, Encoding encoding = null, string projection = null)
: base(shpPath, type, fields, encoding, projection)
{ }
/// <inheritdoc/>
public ShapefilePolygonWriter(string shpPath, ShapeType type, params DbfField[] fields)
: base(shpPath, type, fields)
{
}
internal override ShpWriter<Polygon> CreateShpWriter(Stream shpStream, Stream shxStream)
{
// 回傳 Polygon 的 Writer
return new ShpPolygonWriter(shpStream, shxStream, ShapeType);
}
}Shapefile.cs
最後要調整的地方就是 OpenWrite 裡面的判斷,把 IsPolygon() 的部分獨立出來。
這個過程中最棘手的部分有兩個,一個是要確切地找出原因是甚麼,需要花費一點時間在爬 code;另外一個就是在寫入的地方,因為不確定底層怎麼實作的,所以煩惱了很久要怎麼調整,最後選擇參考 Multi Polygon 的寫法,把不需要的部分(Z point、迴圈)拿掉,幸好有成功 🎊
最後,這個部分我有拉了一個 fork 在我自己的 GitHub 上,如果有需要的人可以參考一下喔 😄