TopoJSON は地理データのコンパクトなエンコーディングで、TopoJSON ファイル内の各フィーチャがフラットなキー‑バリューのペアではなく属性オブジェクトを持つことが一般的です。その構造を CSV にエクスポートする際に問題が発生しがちです。ほとんどのツールはネストされたオブジェクトを削除するか、JSON 文字列として単一セルに詰め込んでしまいます。このガイドでは Aspose.GIS を使用して C# で TopoJSON を CSV に変換する方法 を示します。すべての属性を個別の列としてアドレス可能に保ち、必要に応じてジオメトリを Well‑Known Text として併せて書き出すことができます。
その結果、階層的な GIS データセットがフラットなテーブルに変換され、SQL Server、Power BI、pandas、または単純なスプレッドシートが直接利用でき、アプリケーションにカスタム JSON パース層を追加する必要がなくなります。
Aspose.GIS を使用して TopoJSON を CSV に変換する理由
次のようなプロパティを持つTopoJSONフィーチャを考えてみましょう:
{
"name": "North District",
"prop1": { "this": "that" }
}
素朴な CSV ライターには 3 つの悪い選択肢があります: prop1 を削除する、{"this":"that"} として 1 つのセルにシリアライズする、または失敗する。これらすべては問題を下流に押しやり、誰かがテキスト列から JSON を再解析してからでなければフィルタや結合ができません。
Aspose.GIS は別のアプローチを取ります。TopoJSON ドライバーがファイルを読み込むと、ネストされたオブジェクトをレイヤースキーマにフラットな属性として投影し、そのパス名に基づいて属性名を付け、親と子をアンダースコアで結合します。上記のフィーチャは 2 つの普通の列として公開されます。
| 列 | 値 |
|---|---|
name | 北地区 |
prop1_this | それ |
フラッティングは読み取り時に行われるため、変換ステップでは特別な処理は全く必要ありません。レイヤーが公開するものがそのまま書き込まれるので、prop1_this は CSV に第一級の列として配置され、ソート、フィルタ、結合に使用できます。
TopoJSON から CSV への変換の前提条件
- .NET 6.0 以降、または .NET Framework 4.6.2 以降
- Aspose.GIS for .NET、NuGet からインストール
- 少なくとも 1 つの入れ子属性オブジェクトを含む TopoJSON ファイル
.NET CLI を使用してパッケージをインストールします:
dotnet add package Aspose.GIS
または、Package Manager Consoleから:
Install-Package Aspose.GIS
1つの命名の詳細が人々を混乱させます: NuGet パッケージとアセンブリは Aspose.GIS と呼ばれていますが、ルート名前空間は小文字の is を含む Aspose.Gis です。CSV ドライバオプションは Aspose.Gis.Formats.Csv にあります。
using Aspose.Gis;
using Aspose.Gis.Formats.Csv;
製品の詳細は Aspose.GIS for .NET product page にあり、完全な型一覧は API reference にあります。
変換前にフラット化されたスキーマを検査する
達成すること: 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 ファイルに書き出し、結果を再度読み取って列が正しく渡されたことを確認します。
ステップバイステップのプロセス
- ソースの TopoJSON パスと、宛先の CSV パスを定義します。
VectorLayer.Openを使用してソースを開き、フラット化された属性が存在することを確認します。- ソースと宛先のドライバーを指定して、静的な
VectorLayer.Convertオーバーロードを呼び出します。 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 として含める
達成すること: フラット化された属性 および 各フィーチャのジオメトリを Well-Known Text として保持する列の両方を含む CSV を作成します。
ステップバイステップのプロセス
CsvOptionsのインスタンスを作成し、ColumnWktを選択した列名に設定します。- それを
ConversionOptions.DestinationDriverOptionsに割り当てます。 - 変換オプションを受け取る
VectorLayer.Convertのオーバーロードを呼び出します。 - 同じ
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型で、すべてのドライバ設定オブジェクトの基底クラスです。宛先ドライバがDrivers.Csvの場合にCsvOptionsを渡すことで設定が有効になり、ドライバに対して誤った型のオプションを渡すと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は1つのファイルでも千のファイルでも同一です。並列処理を行う際に覚えておくべき唯一のルールは、各ワーカーが独自のレイヤーとオプションオブジェクトを持つ必要があることです。
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 を捕捉することで、1つの不正な TopoJSON ファイルがバッチを中止する代わりに警告を記録します。
無料ライセンスを取得
Aspose.GIS はライセンスが適用されるまで評価モードで実行されます。無料の一時ライセンスは Aspose の一時ライセンス ページ からリクエストし、アプリケーションの起動時に一度だけ、他の Aspose.GIS の呼び出しの前に適用してください。
var license = new Aspose.Gis.License();
license.SetLicense("Aspose.GIS.NET.lic");
無料の追加リソース
結論
.NET でネストされたプロパティを持つ TopoJSON を CSV に変換するには、1 つの便利な動作に依存します。Aspose.GIS はレイヤーの読み取り中にネストされた属性オブジェクトを prop1_this のような名前付き列にフラット化するため、VectorLayer.Convert でそれらを保持するためのカスタムロジックは不要です。下流システムがジオメトリを必要とする場合は CsvOptions.ColumnWkt を追加し、特定の投影が必要な場合は DestinationSpatialReferenceSystem を追加します。フォルダーを処理する必要がある場合は同じ呼び出しをループします。結果として、数行の C# コードだけでフラットで分析準備が整ったテーブルが生成され、独自の JSON パース層を維持する必要がなくなります。
よくある質問
Aspose.GIS は CSV に変換する際に、入れ子になった JSON オブジェクトをフラット化できますか? はい。TopoJSON ドライバーは、レイヤーが開かれたときに入れ子になった属性オブジェクトを個別の列として公開し、親と子の名前をアンダースコアで結合するため、変換が実行される前にフラット化はすでに行われています。
ネストされたプロパティを処理するためにカスタムコードを書く必要がありますか? いいえ。
VectorLayer.Convertはソースレイヤーが公開するすべての列を書き込むため、ネストされたプロパティのために独自の追加パースコードを書く必要はありません。CSV出力にジオメトリを含めるにはどうすればよいですか?
CsvOptionsのインスタンスを作成し、ColumnWktプロパティを目的の列名に設定し、ConversionOptions.DestinationDriverOptionsに割り当てて、VectorLayer.Convertに渡します。CSVを再度読み込むとジオメトリが空になるのはなぜですか? CSVにはジオメトリ列はありません(要求しない限り)。書き込み時と読み込み時の両方で渡す
CsvOptionsのColumnWktを設定してください。そうしないと、ドライバーはジオメトリを構築する列がありません。変換中に処理すべき例外は何ですか?
ドキュメントで記載されている例外は、null パスの場合のArgumentNullException、オプションオブジェクトがドライバーと一致しない場合のArgumentException、ファイルから機能を読み取れない場合のGisException、および基礎となる I/O エラーの場合のIOExceptionです。TopoJSON ファイルのバッチを一度に変換できますか? はい。ファイルリストをループし、各ファイルに対して
VectorLayer.Convertを呼び出します。ループを並列化する場合は、スレッド間で共有するのではなく、各ワーカーに独自のVectorLayerとオプションインスタンスを割り当ててください。変換は座標系を変更しますか? デフォルトでは変更されません。変換中に再投影するには、
Convertを呼び出す前にConversionOptions.DestinationSpatialReferenceSystemを対象のシステムに設定します。
