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 작성자는 세 가지 나쁜 옵션이 있습니다: 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이지만, 루트 네임스페이스는 소문자 is가 포함된 **Aspose.Gis**입니다. 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 파일에 기록한 다음, 결과를 다시 읽어 열이 제대로 전달되었는지 확인합니다.
단계별 프로세스
- 소스 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는 하나의 파일이든 천 개의 파일이든 동일합니다. 병렬 처리를 할 때 기억해야 할 유일한 규칙은 각 워커가 자체 레이어와 옵션 객체를 가져야 한다는 것입니다.
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 temporary license page에서 무료 임시 라이선스를 요청하고, 다른 Aspose.GIS 호출 전에 애플리케이션 시작 시 한 번 적용하십시오.
var license = new Aspense.Gis.License();
license.SetLicense("Aspose.GIS.NET.lic");
무료 추가 리소스
결론
.NET에서 중첩 속성을 가진 TopoJSON을 CSV로 변환하는 것은 한 가지 유용한 동작에 달려 있습니다: Aspose.GIS는 레이어를 읽는 동안 중첩된 속성 객체를 prop1_this와 같은 명명된 열로 평탄화하므로 VectorLayer.Convert는 이를 보존하기 위한 사용자 정의 로직이 필요하지 않습니다. 다운스트림 시스템이 지오메트리를 필요로 할 때 CsvOptions.ColumnWkt를 추가하고, 특정 투영이 필요할 때 DestinationSpatialReferenceSystem을 추가하며, 폴더를 처리해야 할 경우 동일한 호출을 반복합니다. 결과는 몇 줄의 C# 코드만으로 생성된 평탄하고 분석 준비가 된 테이블이며, 자체 JSON 파싱 레이어를 유지할 필요가 없습니다.
FAQs
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및 옵션 인스턴스를 제공하십시오.변환 시 좌표계가 변경됩니까? 기본적으로는 변경되지 않습니다. 변환 중에 재투영하려면
ConversionOptions.DestinationSpatialReferenceSystem을 대상 시스템으로 설정한 후Convert를 호출하십시오.
