TopoJSON 是地理資料的緊湊編碼,且在 TopoJSON 檔案中,每個特徵通常會攜帶一個屬性物件,而不是平面的鍵值對集合。將此結構匯出為 CSV 時,往往會出現問題:大多數工具要麼會丟棄嵌套物件,要麼會把它作為 JSON 字串塞入單一儲存格。本指南展示了 如何在 C# 中使用 Aspose.GIS 將 TopoJSON 轉換為 CSV,將每個屬性保留為可單獨尋址的欄位,並可選擇將幾何資訊以 Well‑Known Text 形式寫入。

好處在於,層次化的 GIS 資料集會變成一個平面表格,SQL Server、Power BI、pandas 或普通的試算表都能直接使用,且您的應用程式中不需要自訂的 JSON 解析層。

為什麼使用 Aspose.GIS 將 TopoJSON 轉換為 CSV

考慮一個 TopoJSON 特徵,其屬性看起來像這樣:

{
  "name": "North District",
  "prop1": { "this": "that" }
}

一個天真的 CSV 寫入器有三個不好的選擇:丟棄 prop1、將其序列化為 {"this":"that"} 放入單一儲存格,或失敗。這三種方式都會把問題推向下游,必須在有人能在文字欄位中重新解析 JSON 後,才能對其進行篩選或連接。

Aspose.GIS 採取了不同的方法。當 TopoJSON 驅動程式讀取檔案時,它會將巢狀物件投射到圖層結構中,作為一個以其路徑命名的平面屬性,將父層與子層以底線連接。上述特徵會以兩個普通欄位的形式顯示:

欄位值
name北區
prop1_this那

因為扁平化在讀取時發生,轉換步驟根本不需要任何特殊處理。層所暴露的內容就是寫入的內容,因此 prop1_this 會作為第一級欄位出現在 CSV 中,您可以對其進行排序、篩選和連接。

TopoJSON 轉 CSV 轉換的先決條件

  • .NET 6.0 或更新版本,或 .NET Framework 4.6.2 或更新版本
  • Aspose.GIS for .NET,已從 NuGet 安裝
  • 包含至少一個嵌套屬性物件的 TopoJSON 檔案

使用 .NET CLI 安裝套件:

dotnet add package Aspose.GIS

或從套件管理員控制台:

Install-Package Aspose.GIS

有一個命名細節會讓人感到困惑:NuGet 套件和組件名稱為 Aspose.GIS,但根命名空間是 Aspose.Gis,其中的 is 為小寫。CSV 驅動程式的選項位於 Aspose.Gis.Formats.Csv。

using Aspose.Gis;
using Aspose.Gis.Formats.Csv;

產品詳細資訊位於 Aspose.GIS for .NET 產品頁面,完整類型清單請參閱 API 參考文件。

在轉換之前檢查已展平的結構

您將完成的工作: 打開 TopoJSON 圖層並列印其屬性名稱,以便您確切了解 CSV 將包含哪些欄位。

此步驟值得對一個具代表性的檔案執行一次。它會告訴您透過展平產生的實際欄位名稱,這比從原始 JSON 猜測要可靠得多。

using System;
using Aspose.Gis;

using (VectorLayer layer = VectorLayer.Open("data/regions.topojson", Drivers.TopoJson))
{
    Console.WriteLine($"Features: {layer.Count}");

foreach (FeatureAttribute attribute in layer.Attributes)
    {
        Console.WriteLine($"  {attribute.Name} ({attribute.DataType})");
    }
}

對於前面顯示的示例功能,這會列印 name (String) 和 prop1_this (String)。嵌套對象永遠不會作為獨立的屬性出現,這正是您想要的。


將 TopoJSON 轉換為 CSV 並保留嵌套屬性

您將完成的工作: 載入 TopoJSON 檔案,將每個展平的屬性寫入 CSV 檔,然後讀取結果以確認欄位已正確傳遞。

逐步流程

  1. 定義來源 TopoJSON 路徑和目標 CSV 路徑。
  2. 使用 VectorLayer.Open 開啟來源,並確認已展平的屬性存在。
  3. 使用來源和目標驅動程式呼叫靜態 VectorLayer.Convert 重載。
  4. 使用 Drivers.Csv 重新開啟 CSV,並檢查欄位數量和數值。

以下範例將 TopoJSON 檔案轉換為 CSV,並在此過程中展平巢狀屬性。

using System;
using System.IO;
using Aspose.Gis;

class TopoJsonToCsv
{
    static void Main()
    {
        string sourcePath = Path.Combine("data", "regions.topojson");
        string destinationPath = Path.Combine("output", "regions.csv");

Directory.CreateDirectory("output");

// Confirm the nested property was flattened when the layer was opened.
        using (VectorLayer source = VectorLayer.Open(sourcePath, Drivers.TopoJson))
        {
            Console.WriteLine($"Source features: {source.Count}");
            Console.WriteLine($"Has prop1_this: {source.Attributes.Contains("prop1_this")}");
        }

// No options are needed. The conversion writes whatever columns the
        // source layer exposes, flattened attributes included.
        VectorLayer.Convert(sourcePath, Drivers.TopoJson, destinationPath, Drivers.Csv);

// Read the CSV back and verify the schema survived the round trip.
        using (VectorLayer csv = VectorLayer.Open(destinationPath, Drivers.Csv))
        {
            Console.WriteLine($"Rows: {csv.Count}, Columns: {csv.Attributes.Count}");

if (csv.Attributes.Contains("prop1_this"))
            {
                Console.WriteLine($"First value: {csv[0].GetValue<string>("prop1_this")}");
            }

// No WKT column was requested, so features read back without geometry.
            Console.WriteLine($"Geometry empty: {csv[0].Geometry.IsEmpty}");
        }
    }
}

說明

  • VectorLayer.Open(string, FileDriver) 以唯讀方式開啟檔案。Drivers.TopoJson 回傳 TopoJSON 格式的驅動程式。
  • FeatureAttributeCollection.Contains(string) 是測試是否存在指定欄位名稱的最簡便方法。若需要欄位位置,可使用 IndexOf 取得其索引。
  • VectorLayer.Convert(string, FileDriver, string, FileDriver) 是一個靜態輔助方法,可在一次呼叫中將來源資料串流轉換為目標格式。另有接受 ConversionOptions 的重載,於下一節中使用。
  • Feature.GetValue<T>(string) 讀取具型別的屬性值。當欄位可能不存在或為 null 時,請使用 GetValueOrDefault<T>(string),因為 GetValue<T> 假設該值一定存在。
  • 最後的檢查往往讓人感到意外。CSV 檔案本身沒有幾何欄位,除非自行建立;因此每個特徵都會返回空的幾何資訊。這是預設行為,並非轉換失敗,下一節將說明如何解決。

在此情況下,將兩個層級都包裹在 using 區塊中很重要:VectorLayer 實作了 IDisposable,而且在釋放之前,無法保證目標檔案已被刷新並關閉。

使用 CsvOptions 包含幾何作為 WKT

您將完成的目標: 產生一個 CSV,該 CSV 同時包含展平的屬性 以及 一個保存每個要素幾何形狀的 Well-Known Text 列。

逐步流程

  1. 建立一個 CsvOptions 實例,並將 ColumnWkt 設定為您選擇的欄位名稱。
  2. 將其指派給 ConversionOptions.DestinationDriverOptions。
  3. 呼叫接受轉換選項的 VectorLayer.Convert 重載。
  4. 使用相同的 CsvOptions 重新開啟 CSV,讓驅動程式知道要將哪個欄位解析為幾何圖形。
using System;
using System.IO;
using Aspose.Gis;
using Aspose.Gis.Formats.Csv;

class TopoJsonToCsvWithGeometry
{
    static void Main()
    {
        string sourcePath = Path.Combine("data", "regions.topojson");
        string destinationPath = Path.Combine("output", "regions-wkt.csv");

Directory.CreateDirectory("output");

// Ask the CSV driver for a geometry column named "GeometryWkt".
        var csvOptions = new CsvOptions
        {
            ColumnWkt = "GeometryWkt"
        };

var conversionOptions = new ConversionOptions
        {
            DestinationDriverOptions = csvOptions
        };

VectorLayer.Convert(
            sourcePath,
            Drivers.TopoJson,
            destinationPath,
            Drivers.Csv,
            conversionOptions);

// Pass the same options when reading, or the WKT column is just text.
        using (VectorLayer layer = VectorLayer.Open(destinationPath, Drivers.Csv, csvOptions))
        {
            foreach (Feature feature in layer)
            {
                string name = feature.GetValueOrDefault<string>("name");
                string wkt = feature.Geometry.IsEmpty ? "(empty)" : feature.Geometry.AsText();

Console.WriteLine($"{name}: {wkt}");
            }
        }
    }
}

說明

  • CsvOptions.ColumnWkt 命名了保存每個要素幾何形狀(以 WKT 形式)的欄位。預設為 null,將其保持為 null 或空值正是導致先前範例未產生幾何形狀的原因。
  • ConversionOptions.DestinationDriverOptions 的類型為 DriverOptions,它是所有驅動程式設定物件的基礎類別。將 CsvOptions 傳遞給目的驅動程式為 Drivers.Csv 時,設定才會生效;若為驅動程式傳遞錯誤類型的選項,會拋出 ArgumentException。
  • 同一個 CsvOptions 實例在讀取端會被重複使用。若沒有它,CSV 驅動程式就沒有理由將 GeometryWkt 視為除另一個字串欄位之外的其他類型。
  • IGeometry.AsText() 會回傳 ISO WKT 表示形式,因此列印的輸出與寫入檔案的內容相符。

對稱性是關鍵:屬性欄位在是否包含幾何資訊時表現相同,因此您可以在不影響管道其他部分的情況下,開啟或關閉 WKT 欄位。

在轉換期間重新投影

如果消費系統需要特定的坐標系統,請在轉換過程中提出需求,而不是在 CSV 之後進行後處理。ConversionOptions 會攜帶空間參考以及驅動程式選項:

var conversionOptions = new ConversionOptions
{
    DestinationDriverOptions = new CsvOptions { ColumnWkt = "GeometryWkt" },
    DestinationSpatialReferenceSystem = SpatialReferenceSystem.Wgs84
};

VectorLayer.Convert(sourcePath, Drivers.TopoJson, destinationPath, Drivers.Csv, conversionOptions);

並非所有目標驅動程式都支援每個空間參考系統。如果目標系統可在執行時配置,請先呼叫 Drivers.Csv.SupportsSpatialReferenceSystem(...),若不支援則回退到 null 選項物件。

批次轉換資料夾中的 TopoJSON 檔案

API 在處理單個檔案和千個檔案時是相同的。當您平行化時,唯一值得記住的規則是每個工作者都需要自己的層和選項物件。

using System;
using System.IO;
using System.Threading.Tasks;
using Aspose.Gis;
using Aspose.Gis.Formats.Csv;

static void ConvertFolder(string inputFolder, string outputFolder)
{
    Directory.CreateDirectory(outputFolder);

var files = Directory.GetFiles(inputFolder, "*.topojson");

Parallel.ForEach(files, file =>
    {
        string destination = Path.Combine(
            outputFolder,
            Path.GetFileNameWithoutExtension(file) + ".csv");

// Fresh options per iteration: nothing is shared across threads.
        var options = new ConversionOptions
        {
            DestinationDriverOptions = new CsvOptions { ColumnWkt = "GeometryWkt" }
        };

try
        {
            VectorLayer.Convert(file, Drivers.TopoJson, destination, Drivers.Csv, options);
        }
        catch (GisException ex)
        {
            Console.Error.WriteLine($"Skipped {Path.GetFileName(file)}: {ex.Message}");
        }
    });
}

對每個檔案捕獲 GisException 而不是在整個迴圈周圍捕獲,意味著單個格式錯誤的 TopoJSON 檔案只會記錄警告,而不會中止批次。

取得免費授權

Aspose.GIS 在應用許可證之前會以評估模式運行。請從 Aspose 臨時許可證頁面 申請免費的臨時許可證,並在應用程式啟動時一次性套用,於任何其他 Aspose.GIS 呼叫之前執行:

var license = new Aspose.Gis.License();
license.SetLicense("Aspose.GIS.NET.lic");

免費的額外資源

結論

在 .NET 中將具有巢狀屬性的 TopoJSON 轉換為 CSV,核心在於一個有用的行為:Aspose.GIS 在讀取圖層時會將巢狀屬性物件展平為具名欄位,例如 prop1_this,因此 VectorLayer.Convert 不需要自訂邏輯來保留它們。當下游系統需要幾何資訊時加入 CsvOptions.ColumnWkt,當需要特定投影時加入 DestinationSpatialReferenceSystem,並在需要處理資料夾時重複呼叫同一方法。最終產生的是一個平坦、可直接分析的表格,只需少量 C# 程式碼,且無需自行維護 JSON 解析層。

常見問題

  1. Aspose.GIS 在轉換為 CSV 時能否展平嵌套的 JSON 物件? 是的。TopoJSON 驅動程式在打開圖層時,會將嵌套的屬性物件顯示為獨立的欄位,並以底線將父層與子層名稱連接起來,因此在執行轉換之前,展平已經完成。

  2. 我需要編寫自訂程式碼來處理巢狀屬性嗎? 不需要。VectorLayer.Convert 會寫入來源圖層所公開的任何欄位,因此巢狀屬性不需要您自行額外的解析程式碼。

  3. 如何在 CSV 輸出中包含幾何資訊? 建立一個 CsvOptions 實例,將其 ColumnWkt 屬性設定為您想要的欄位名稱,將其指派給 ConversionOptions.DestinationDriverOptions,然後將其傳遞給 VectorLayer.Convert。

  4. 為什麼在重新讀取 CSV 時幾何資訊是空的? CSV 檔案本身沒有幾何欄位,除非你特別要求。請在寫入和讀取時傳遞的 CsvOptions 上設定 ColumnWkt,否則驅動程式將沒有欄位可用來建立幾何資訊。

  5. 在轉換過程中應該處理哪些例外? 文件中列出的例外包括 ArgumentNullException(當路徑為 null 時),ArgumentException(當選項物件與驅動程式不匹配時),GisException(當無法從檔案中讀取特徵時),以及 IOException(底層 I/O 失敗時)。

  6. 我可以一次性轉換一批 TopoJSON 檔案嗎? 是的。遍歷檔案清單,對每個檔案呼叫 VectorLayer.Convert。如果將迴圈平行化,請為每個工作執行緒提供各自的 VectorLayer 和 options 實例,而不要在執行緒之間共享同一個實例。

  7. 轉換會改變座標系統嗎? 預設情況下不會。若要在轉換過程中重新投影,請在呼叫 Convert 之前將 ConversionOptions.DestinationSpatialReferenceSystem 設定為目標系統。

閱讀更多