> ## Content Index
> Fetch the complete content index at: https://blog.anthea.day/llms.txt
> Use this file to discover other available public pages before exploring further.

# NetTopologySuite 處理 GIS (1)：讀寫 Shapefile
- URL: https://blog.anthea.day/series-net-topology-suite-1/
- Published: 2022-08-19T06:47:00.000Z
- Updated: 2026-10-21T04:08:28.000Z
- Author: Anthea / 芫荽
- Tags: #Migrated-1792241843742, #Import 2026-10-17 12:57, Coding, NetTopologySuite 處理 GIS

上次使用 GDAL 進行 Shapefile 檔案的讀取之後，在實務上又碰到其他問題了。有個需求是要針對點位去找出他屬於哪一塊土地，原本想說把檔案內的資料一次讀出來，在透過程式去處理就好，但是 GDAL 有一個問題：沒有註冊、沒有開啟 Shapefile，貌似就不能進行 Geometry 的相關操作，如判斷交集、新增 Buffer 之類的，直接把我的美好幻想打碎 💔

為了解決這個問題，我使用了另外一個也相當有名的套件 — — `NetTopologySuite`，針對我從 Shapefile 裡取出的 WKT，去進行轉換和比對。但一個專案裡面使用兩個性質相同的套件，我個人是覺得很沒必要，所以決定統一使用 `NetTopologySuite` 作為地理資訊操作的主要套件。接下來就一起看看如何使用 `NetTopologySuite` 吧。

---

### 安裝 `NetTopologySuite.IO.Esri`

如果你使用 `NetTopologySuite` 和 `Shapefile` 這兩個關鍵字去 google ，第一個應該會看到的是 `NetTopologySuite.IO.ShapeFile` 這個套件，但可以在 `README` 的地方看到團隊沒有要繼續更新了，建議轉而使用 `NetTopologySuite.IO.Esri`。但經過搜尋之後，發現他還沒有被發佈到 Nuget 上，所以必須手動安裝。

安裝的方法很簡單，在 GitHub 頁面選擇下載成 zip 檔，或是 clone 下來之後，按照個人習慣（我都用 `dotnet publish -c Release` ）將他編譯成 dll 檔案，接著在你原本的專案內新增這些 dll 檔案的參考即可。

圖為編譯後的全部內容，選取起來的三個 dll 中，最主要是第三個 `NetTopologySuite.IO.Esri.Shapefile`，其他兩個也會使用到，可以從這裡加入參考，或是從 Nuget 中加入也行。

![](https://cdn-images-1.medium.com/max/800/1*V_05RWDx6pii4Xavkwrngg.png)

#### 新增參考

如果你是使用 Visual Stadio 可以看看[官方文件](https://docs.microsoft.com/zh-tw/visualstudio/ide/how-to-add-or-remove-references-by-using-the-reference-manager?view=vs-2022&ref=blog.anthea.day)；如果你和我一樣是使用 Rider，對著你的 `project` 右鍵，點選 `Add`，就可以看到 `Add Reference` 的選項。點擊之後會打開一個視窗，在視窗最下面點選 `Add From …` 就能自行新增參考了。

![](https://cdn-images-1.medium.com/max/800/1*btYVM3Rs6gAUEjFsGZTtkA.png)

對著 project 右鍵後的畫面

![](https://cdn-images-1.medium.com/max/800/1*HJK4PJ0UDqIisaJ8WoTPOA.png)

圖片左邊為新增完成後的樣子，右邊為點開新增參考視窗的樣子

---

### Shapefile 操作

將 `NetTopologySuite.IO.Esri` 加入我們的專案之後，就可以開始對我們的 Shapefile 進行操作了。

#### 讀取

```C#
using NetTopologySuite.IO.Esri;

foreach (var feature in Shapefile.ReadAllFeatures(@"C:\Users\emct\Downloads\taiwan\All\4326\COUNTY_MOI_1090820_4326.shp"))
{
    foreach (var attrName in feature.Attributes.GetNames())
    {
        Console.WriteLine($"{attrName,10}: {feature.Attributes[attrName]}");
    }

    // Console.WriteLine($"     SHAPE: {feature.Geometry.AsText()}");
    Console.WriteLine($"      SRID: {feature.Geometry.SRID}");
    Console.WriteLine();
}
```

根據 GitHub 上的寫法，只要簡單的這幾行，馬上就能讀取 Shapefile 了！執行後的結果如下：

![](https://cdn-images-1.medium.com/max/800/1*EICmt-q1-Bp1Vvs2mkswgA.png)

讀取 Shapefile 後的結果

可以看到之前讓我們在 GDAL 中痛苦不已的中文，在 `NetTopologySuite` 上居然很正常的顯示了！（痛哭流涕）

另外也可以看到，`NetTopologySuite` 在寫法上相對來說更為簡潔，直接讀取 `Feature` 就好，再透過欄位名稱從 `Atrributes` 取得 `Field` 的值，不像 GDAL 還需要從 `Layer` 先取得 `Layer` 的數量跑回圈，接著從 `Layer` 拿 `layer.GetNextFeature()`，看起來好看多了！

#### 輸出

```C#
using System.Text;
using NetTopologySuite.Features;
using NetTopologySuite.IO;
using NetTopologySuite.IO.Esri;

var features = new List<Feature>();
var wktReader = new WKTReader();

var fcuGeometry = wktReader.Read(
    "POLYGON((214094.18733422138 2675204.397772933,214613.04806256315 2675203.096234374,214612.0331897791 2674795.5200602347,214093.15758024747 2674796.8214499434,214094.18733422138 2675204.397772933))");

var fcuAttributes = new AttributesTable();
fcuAttributes.Add("日期", DateTime.Today);
fcuAttributes.Add("地點", "逢甲大學");

var fcuFeature = new Feature(fcuGeometry, fcuAttributes);
features.Add(fcuFeature);

var nightMarketGeometry = wktReader.Read(
    "POLYGON((213945.3342767608 2674876.347737663,214097.93845940504 2674873.793433358,214099.4943339883 2674626.6362772766,214474.48457545802 2674625.694102451,214511.39058478415 2674562.7289472898,214223.11502519806 2674368.326804782,213983.77470059253 2674559.719947559,213945.3342767608 2674876.347737663))");
var nightMarketAttributes = new AttributesTable();
nightMarketAttributes.Add("日期", DateTime.Today);
nightMarketAttributes.Add("地點", "逢甲夜市");
var nightMarketFeature = new Feature(nightMarketGeometry, nightMarketAttributes);
features.Add(nightMarketFeature);

// 指定的 EPSG 字串，可以在 https://epsg.io/ 找到
var wkt = "PROJCS[\"TWD97 / TM2 zone 121\",GEOGCS[\"TWD97\",DATUM[\"Taiwan_Datum_1997\",SPHEROID[\"GRS 1980\",6378137,298.257222101],TOWGS84[0,0,0,0,0,0,0]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"3824\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",121],PARAMETER[\"scale_factor\",0.9999],PARAMETER[\"false_easting\",250000],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],AUTHORITY[\"EPSG\",\"3826\"]]";

Shapefile.WriteAllFeatures(features, @"files\create.shp", Encoding.UTF8, wkt);
```

根據 GitHub 上的範例，使用 `Shapefile.WriteAllFeatures()` 來建立，執行之後遇到了一個錯誤：

![](https://cdn-images-1.medium.com/max/800/1*1bPLF02NQKPToKsUIlomKg.png)

執行後在 WriteAllFeatures 中遇到的 Exception

要解決這個問題需要花一點篇幅進行，所以會另外開一篇來講解，包含為甚麼會有這個錯誤，以及如何修改原始碼讓我們可以成功建立一個 Shapefile。

---

#### 參考文章

- [NetTopologySuite.IO.Esri GitHub](https://github.com/NetTopologySuite/NetTopologySuite.IO.Esri?ref=blog.anthea.day)