GDAL 操作 ShapeFile (1):SHP 讀取
雖然公司的產品以 APP 為主,但身為一個後端工程師,名片上掛著 GIS 的頭銜,一些基本的地理資訊技術還是要稍微碰一下的。平常接觸最多的就是有紀錄空間資訊的 Shapefile,會需要進行的操作包含讀取它的空間資訊、每個空間資訊上的屬性,以及利用空間資訊做交集或包含的比對。
一個 Shape 文件包括三個文件:一個主文件.shp,一個索引文件.shx,和一個 dBASE 表.dbf。主文件是一個直接存取,變長度紀錄的文件,其中每個紀錄描述構成一個地理特征Feature的所有vertices座標值。在索引文件中,每條記錄包含對應主文件記錄距離主文件頭開始的偏移量,dBASE 表包含 SHP 文件中每一個 Feature 的特徵屬性,表中幾何紀錄和屬性數據之間的一一對應關係是基於記錄數目的 ID。在 dBASE 文件中的屬性記錄必須和主文件中的記錄顺序是相同的。圖形數據和屬性數據通過索引號建立一一對應的關係。
— — — — — — — — — — — — — — — —
版權聲明:本文為 CSDN 博主「gis0911178」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連結及本聲明。
原文連結:https://blog.csdn.net/gis0911178/article/details/52162072
我對 Shapefile 的理解也還不透徹,但從上面擷取的文章中可以看出一件事情是:一個正常的 Shapefile 檔案必須至少包含三個副檔名的檔案,而其中在我的工作上,會需要取得的資料,一是在 .shp 中記錄的空間資訊,二是在 .dbf 中記錄的其他屬性。不過要注意的是,.shp 檔案是無法單獨被讀取的,如果要對 Shapefile 進行任何操作,同一個資料夾底下就必須同時存在上面所提到的三個檔案,才能夠順利讀取喔。
DotSpatial V.S. GDAL
前輩留下來的程式碼中,使用的都是 DotSpatial 這個套件,所以一開始我也是跟著使用,但後來遇到一個很神祕的問題:在三個不同的環境上,我生產出來的 Shapefile Feature 內容會不一樣,local 端有完整的 100 多個,移到正式環境上卻只剩下不到 10 個 😢 由於那時候一直研究不出個所以然,不想浪費太多時間果斷的換了現在使用的套件 GDAL。
.NET Framework 和 .NET Core 所安裝的 Nuget 套件不太相同。如果是使用 .NET Framework,需要安裝的是 Gdal 和 Gdal.Native,安裝完成後會自動幫你長出兩個 Configuration 的檔案,沒記錯的話一個是 .cs,一個是 VB 的,只要留著 .cs 的就好,並且把命名空間改成你自己的命名空間,就可以開始使用了。至於 .NET 的話,要安裝的是 Gdal.Core,才有辦法正確使用,否則會出現 Unable to load DLL 'gdal_wrap' or one of its dependencies 的相關錯誤喔。
讀取 Shapefile 檔案
我利用 QGIS 製作了一個簡單的 Shapefile 檔案,只有一個 Feature,包含的內容如下:

讀取的程式碼如下:
// 註冊 GDAL
Gdal.AllRegister();
Ogr.RegisterAll();
// 開啟 SHP 檔案(0 - 讀取、1 - 讀寫)
var source = Ogr.Open("files/simple/test.shp", (int)EnumShapefileOpen.ReadOnly);
var layerCount = source.GetLayerCount();
for (var i = 0; i < layerCount; i++)
{
// 讀取圖層 Layer 資訊
var layer = source.GetLayerByIndex(i);
var fieldCount = layer.GetLayerDefn().GetFieldCount();
for (var j = 0; j < fieldCount; j++)
{
var field = layer.GetLayerDefn().GetFieldDefn(j);
var fieldName = field.GetName();
Console.WriteLine($"欄位{j + 1}. {fieldName}");
}
// 讀取要素 Feature
Feature feature;
while ((feature = layer.GetNextFeature()) != null)
{
var id = feature.GetFieldAsInteger(0);
Console.WriteLine($"內容1. Id: {id}");
var name = feature.GetFieldAsString(1);
Console.WriteLine($"內容2. 土地名稱: {name}");
var area = feature.GetFieldAsInteger64(2);
Console.WriteLine($"內容3. 土地面積: {area}");
// 轉換成 WKT 的值
var geometry = feature.GetGeometryRef();
geometry.ExportToWkt(out var wkt);
Console.WriteLine($"- WKT: {wkt}");
}
}
public enum EnumShapefileOpen
{
ReadOnly
}執行結果如下:

可以看到,內容的值是中文,卻變成了亂碼。而明明欄位的名稱原本也是中文,但好好地顯示。WHY?
相關的錯誤修正會在下一篇提到。
參考文章:C#不用ArcEngine,生成Shp文件(一) — — — — -shapefile数据说明