Skip to content
Coding

GDAL 操作 ShapeFile (1):SHP 讀取

GDAL 操作 ShapeFile (1):SHP 讀取
讀取 Shapefile 的執行結果

雖然公司的產品以 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,包含的內容如下:

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
}

執行結果如下:

讀取 Shapefile 的執行結果

可以看到,內容的值是中文,卻變成了亂碼。而明明欄位的名稱原本也是中文,但好好地顯示。WHY?

相關的錯誤修正會在下一篇提到。


參考文章:C#不用ArcEngine,生成Shp文件(一) — — — — -shapefile数据说明