第06章 - 数据格式转换实战
第06章 - 数据格式转换实战
6.1 格式转换概述
6.1.1 支持的格式
OGU4Net通过GDAL支持多种矢量数据格式:
| 格式 | 枚举值 | 扩展名 | 说明 |
|---|---|---|---|
| Shapefile | SHP | .shp | 最常用的矢量格式 |
| GeoJSON | GEOJSON | .geojson/.json | Web GIS标准格式 |
| GeoPackage | GEOPACKAGE | .gpkg | OGC SQLite标准 |
| File Geodatabase | FILEGDB | .gdb | Esri文件地理数据库 |
| PostGIS | POSTGIS | - | PostgreSQL空间扩展 |
| KML | KML | .kml | Google Earth格式 |
| DXF | DXF | .dxf | AutoCAD交换格式 |
| 国土TXT | TXT | .txt | 自然资源部门格式 |
6.1.2 转换原理
格式转换的基本流程:
源格式文件 → 读取器 → OguLayer → 写入器 → 目标格式文件
↓ ↓ ↓ ↓
GDAL/OGR GdalReader 统一模型 GdalWriter
核心代码:
// 基本转换
public static void ConvertFormat(
string inputPath,
DataFormatType inputFormat,
string outputPath,
DataFormatType outputFormat)
{
// 1. 读取
var layer = OguLayerUtil.ReadLayer(inputFormat, inputPath);
// 2. 写入
OguLayerUtil.WriteLayer(outputFormat, layer, outputPath);
}
6.2 Shapefile格式
6.2.1 读取Shapefile
using OpenGIS.Utils.DataSource;
using OpenGIS.Utils.Engine.Enums;
using OpenGIS.Utils.Engine.Util;
// 基本读取
var layer = OguLayerUtil.ReadLayer(DataFormatType.SHP, "data/cities.shp");
Console.WriteLine($"图层名: {layer.Name}");
Console.WriteLine($"几何类型: {layer.GeometryType}");
Console.WriteLine($"要素数: {layer.GetFeatureCount()}");
Console.WriteLine($"字段数: {layer.Fields.Count}");
// 使用ShpUtil读取(自动处理编码)
var layerWithEncoding = ShpUtil.ReadShapefile("data/cities.shp");
// 指定编码读取
var gbkLayer = ShpUtil.ReadShapefile("data/cities.shp", Encoding.GetEncoding("GBK"));
6.2.2 写入Shapefile
// 基本写入
OguLayerUtil.WriteLayer(DataFormatType.SHP, layer, "output/cities.shp");
// 使用ShpUtil写入(自动创建CPG文件)
ShpUtil.WriteShapefile(layer, "output/cities.shp", Encoding.UTF8);
6.2.3 编码处理
Shapefile的中文编码是常见问题:
using OpenGIS.Utils.Utils;
// 检测Shapefile编码
var encoding = ShpUtil.GetShapefileEncoding("data.shp");
Console.WriteLine($"检测到编码: {encoding.WebName}");
// 创建CPG文件(指定编码)
ShpUtil.CreateCpgFile("output.shp", Encoding.UTF8);
// 转换编码
EncodingUtil.ConvertFileEncoding("data.dbf", Encoding.UTF8);
6.2.4 Shapefile附属文件
// Shapefile包含多个文件
// .shp - 几何数据
// .shx - 几何索引
// .dbf - 属性数据
// .prj - 坐标系定义
// .cpg - 编码定义(可选)
// 检查Shapefile是否完整
bool IsShapefileComplete(string shpPath)
{
var baseName = Path.GetFileNameWithoutExtension(shpPath);
var dir = Path.GetDirectoryName(shpPath) ?? ".";
var requiredExtensions = new[] { ".shp", ".shx", ".dbf" };
return requiredExtensions.All(ext =>
File.Exists(Path.Combine(dir, baseName + ext)));
}
6.3 GeoJSON格式
6.3.1 读取GeoJSON
// 读取GeoJSON文件
var layer = OguLayerUtil.ReadLayer(
DataFormatType.GEOJSON,
"data/cities.geojson"
);
// 遍历要素
foreach (var feature in layer.Features)
{
Console.WriteLine($"FID: {feature.Fid}");
Console.WriteLine($"Geometry: {feature.Wkt}");
foreach (var attr in feature.Attributes)
{
Console.WriteLine($" {attr.Key}: {attr.Value.Value}");
}
}
6.3.2 写入GeoJSON
// 写入GeoJSON
OguLayerUtil.WriteLayer(
DataFormatType.GEOJSON,
layer,
"output/cities.geojson"
);
6.3.3 GeoJSON特点
GeoJSON格式的特点:
- 纯文本,易于阅读和编辑
- Web友好,JavaScript直接解析
- 自描述,包含完整的属性信息
- 默认使用WGS84坐标系(EPSG:4326)
// GeoJSON示例结构
/*
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [116.404, 39.915]
},
"properties": {
"name": "北京",
"population": 21540000
}
}
]
}
*/
6.3.4 单个几何转GeoJSON
using OpenGIS.Utils.Geometry;
// WKT转GeoJSON
var wkt = "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))";
var geojson = GeometryUtil.Wkt2Geojson(wkt);
Console.WriteLine(geojson);
// 输出:{ "type": "Polygon", "coordinates": [ [ [ 0.0, 0.0 ], [ 10.0, 0.0 ], ... ] ] }
6.4 GeoPackage格式
6.4.1 读取GeoPackage
// 读取GeoPackage
var layer = OguLayerUtil.ReadLayer(
DataFormatType.GEOPACKAGE,
"data/cities.gpkg",
layerName: "cities" // GeoPackage可包含多个图层
);
// 获取GeoPackage中的所有图层名
var layerNames = OguLayerUtil.GetLayerNames(
DataFormatType.GEOPACKAGE,
"data/cities.gpkg"
);
Console.WriteLine("GeoPackage中的图层:");
foreach (var name in layerNames)
{
Console.WriteLine($" - {name}");
}
6.4.2 写入GeoPackage
// 写入GeoPackage
OguLayerUtil.WriteLayer(
DataFormatType.GEOPACKAGE,
layer,
"output/cities.gpkg",
layerName: "my_cities"
);
// GeoPackage可以包含多个图层
// 需要使用追加模式(当前未实现)
6.4.3 GeoPackage特点
- OGC标准的SQLite数据库格式
- 支持矢量和栅格数据
- 单文件,便于分发
- 支持多图层
- 支持元数据和样式
6.5 FileGDB格式
6.5.1 读取FileGDB
// 检查驱动可用性
var hasFileGDB = GdalConfiguration.IsDriverAvailable("FileGDB");
var hasOpenFileGDB = GdalConfiguration.IsDriverAvailable("OpenFileGDB");
if (hasFileGDB || hasOpenFileGDB)
{
// 读取FileGDB
var layer = OguLayerUtil.ReadLayer(
DataFormatType.FILEGDB,
"data/sample.gdb",
layerName: "Cities"
);
Console.WriteLine($"读取到 {layer.GetFeatureCount()} 个要素");
}
else
{
Console.WriteLine("FileGDB驱动不可用");
}
// 获取FileGDB中的图层列表
var layers = OguLayerUtil.GetLayerNames(
DataFormatType.FILEGDB,
"data/sample.gdb"
);
6.5.2 写入FileGDB
// 注意:写入FileGDB需要FileGDB驱动,OpenFileGDB只支持读取
if (GdalConfiguration.IsDriverAvailable("FileGDB"))
{
OguLayerUtil.WriteLayer(
DataFormatType.FILEGDB,
layer,
"output/sample.gdb",
layerName: "MyLayer"
);
}
else
{
Console.WriteLine("FileGDB驱动不可用,无法写入");
// 可以考虑转换为其他格式
OguLayerUtil.WriteLayer(
DataFormatType.GEOPACKAGE,
layer,
"output/sample.gpkg"
);
}
6.6 PostGIS格式
6.6.1 连接PostGIS
// PostGIS连接字符串格式
// PG:host=<host> dbname=<database> user=<user> password=<password> [port=<port>]
string connStr = "PG:host=localhost dbname=mydb user=postgres password=secret";
// 读取PostGIS表
var layer = OguLayerUtil.ReadLayer(
DataFormatType.POSTGIS,
connStr,
layerName: "public.cities" // schema.table
);
Console.WriteLine($"读取到 {layer.GetFeatureCount()} 个要素");
6.6.2 PostGIS查询
// 带过滤条件读取
var layer = OguLayerUtil.ReadLayer(
DataFormatType.POSTGIS,
connStr,
layerName: "public.cities",
attributeFilter: "population > 1000000"
);
// 空间过滤
var bbox = "POLYGON ((116.0 39.0, 117.0 39.0, 117.0 40.0, 116.0 40.0, 116.0 39.0))";
var layerFiltered = OguLayerUtil.ReadLayer(
DataFormatType.POSTGIS,
connStr,
layerName: "public.cities",
spatialFilterWkt: bbox
);
6.6.3 写入PostGIS
// 写入到PostGIS(会创建新表)
OguLayerUtil.WriteLayer(
DataFormatType.POSTGIS,
layer,
connStr,
layerName: "public.new_cities"
);
6.6.4 PostgisUtil工具类
using OpenGIS.Utils.Engine.Util;
// PostgisUtil提供更多PostGIS专用功能
// 例如:连接池管理、批量操作等
6.7 KML格式
6.7.1 读取KML
// 读取KML文件
var layer = OguLayerUtil.ReadLayer(
DataFormatType.KML,
"data/places.kml"
);
// 遍历要素
foreach (var feature in layer.Features)
{
var name = feature.GetValue("Name");
var description = feature.GetValue("description");
Console.WriteLine($"{name}: {description}");
}
6.7.2 写入KML
// 写入KML
OguLayerUtil.WriteLayer(
DataFormatType.KML,
layer,
"output/places.kml"
);
6.7.3 KML特点
- Google Earth和Google Maps使用的格式
- 基于XML
- 支持样式和3D属性
- 适合地图展示和分享
6.8 DXF格式
6.8.1 读取DXF
// 读取DXF文件
var layer = OguLayerUtil.ReadLayer(
DataFormatType.DXF,
"data/drawing.dxf"
);
// DXF通常包含多个图层(CAD图层)
var layerNames = OguLayerUtil.GetLayerNames(
DataFormatType.DXF,
"data/drawing.dxf"
);
foreach (var name in layerNames)
{
Console.WriteLine($"CAD图层: {name}");
}
6.8.2 写入DXF
// 写入DXF
OguLayerUtil.WriteLayer(
DataFormatType.DXF,
layer,
"output/drawing.dxf"
);
6.9 国土TXT格式
6.9.1 格式说明
国土TXT格式是中国自然资源部门使用的坐标文件格式:
数据来源: 自然资源部
坐标系: CGCS2000
分带: 3度带
投影类型: 高斯-克吕格投影
单位: 米
点号 圈号 X Y Z 备注
J1 1 456789.123 4321098.456 界址点1
J2 1 456790.234 4321099.567 界址点2
...
6.9.2 读取TXT
using OpenGIS.Utils.DataSource;
// 读取国土TXT文件
var layer = GtTxtUtil.LoadTxt("data/boundary.txt");
Console.WriteLine($"图层名: {layer.Name}");
Console.WriteLine($"要素数: {layer.GetFeatureCount()}");
// 读取元数据
if (layer.Metadata != null)
{
Console.WriteLine($"坐标系: {layer.Metadata.CoordinateSystemName}");
Console.WriteLine($"分带: {layer.Metadata.ZoneDivision}");
Console.WriteLine($"单位: {layer.Metadata.MeasureUnit}");
}
// 遍历坐标点
foreach (var feature in layer.Features)
{
var pointNumber = feature.GetValue("点号");
var ringNumber = feature.GetValue("圈号");
var x = feature.GetAttribute("X")?.GetDoubleValue();
var y = feature.GetAttribute("Y")?.GetDoubleValue();
Console.WriteLine($"{pointNumber} ({ringNumber}): X={x}, Y={y}");
}
6.9.3 写入TXT
// 创建元数据
var metadata = new OguLayerMetadata
{
DataSource = "测量队",
CoordinateSystemName = "CGCS2000",
ZoneDivision = "3度带",
ProjectionType = "高斯-克吕格投影",
MeasureUnit = "米"
};
// 写入TXT文件
GtTxtUtil.SaveTxt(layer, "output/boundary.txt", metadata);
6.9.4 TXT转Shapefile
// 1. 读取TXT
var txtLayer = GtTxtUtil.LoadTxt("boundary.txt");
// 2. TXT是点数据,如果需要转为面,需要重组几何
// 根据圈号分组点
var rings = txtLayer.Features
.GroupBy(f => f.GetValue("圈号")?.ToString() ?? "1")
.OrderBy(g => g.Key)
.ToList();
// 3. 创建多边形图层
var polyLayer = new OguLayer
{
Name = "Boundary",
GeometryType = GeometryType.POLYGON,
Wkid = 4490 // CGCS2000
};
polyLayer.AddField(new OguField { Name = "Name", DataType = FieldDataType.STRING, Length = 50 });
// 4. 构建多边形WKT
var coordinates = new List<string>();
foreach (var ring in rings)
{
var points = ring.OrderBy(f => f.GetValue("点号")?.ToString())
.Select(f =>
{
var x = f.GetAttribute("X")?.GetDoubleValue() ?? 0;
var y = f.GetAttribute("Y")?.GetDoubleValue() ?? 0;
return $"{x} {y}";
})
.ToList();
// 闭合环
if (points.First() != points.Last())
points.Add(points.First());
coordinates.Add($"({string.Join(", ", points)})");
}
var wkt = $"POLYGON ({string.Join(", ", coordinates)})";
var feature = new OguFeature { Fid = 1, Wkt = wkt };
feature.SetValue("Name", txtLayer.Name);
polyLayer.AddFeature(feature);
// 5. 写入Shapefile
OguLayerUtil.WriteLayer(DataFormatType.SHP, polyLayer, "output/boundary.shp");
6.10 批量转换
6.10.1 目录批量转换
public static void BatchConvert(
string inputDir,
string outputDir,
DataFormatType inputFormat,
DataFormatType outputFormat,
string inputExtension,
string outputExtension)
{
// 确保输出目录存在
Directory.CreateDirectory(outputDir);
// 获取所有输入文件
var inputFiles = Directory.GetFiles(inputDir, $"*{inputExtension}");
Console.WriteLine($"找到 {inputFiles.Length} 个文件需要转换");
int success = 0, failed = 0;
foreach (var inputFile in inputFiles)
{
var fileName = Path.GetFileNameWithoutExtension(inputFile);
var outputFile = Path.Combine(outputDir, $"{fileName}{outputExtension}");
try
{
Console.Write($"转换: {fileName}...");
OguLayerUtil.ConvertFormat(
inputFile, inputFormat,
outputFile, outputFormat
);
Console.WriteLine(" 成功");
success++;
}
catch (Exception ex)
{
Console.WriteLine($" 失败: {ex.Message}");
failed++;
}
}
Console.WriteLine($"\n转换完成: 成功 {success}, 失败 {failed}");
}
// 使用示例
BatchConvert(
"input/",
"output/",
DataFormatType.SHP,
DataFormatType.GEOJSON,
".shp",
".geojson"
);
6.10.2 异步批量转换
public static async Task BatchConvertAsync(
string[] inputFiles,
string outputDir,
DataFormatType inputFormat,
DataFormatType outputFormat,
string outputExtension,
int maxParallel = 4)
{
Directory.CreateDirectory(outputDir);
var semaphore = new SemaphoreSlim(maxParallel);
var tasks = new List<Task>();
foreach (var inputFile in inputFiles)
{
await semaphore.WaitAsync();
var task = Task.Run(async () =>
{
try
{
var fileName = Path.GetFileNameWithoutExtension(inputFile);
var outputFile = Path.Combine(outputDir, $"{fileName}{outputExtension}");
await OguLayerUtil.ConvertFormatAsync(
inputFile, inputFormat,
outputFile, outputFormat
);
Console.WriteLine($"完成: {fileName}");
}
finally
{
semaphore.Release();
}
});
tasks.Add(task);
}
await Task.WhenAll(tasks);
}
6.11 转换中的数据处理
6.11.1 坐标转换
// 读取WGS84坐标数据
var layer = OguLayerUtil.ReadLayer(DataFormatType.SHP, "wgs84.shp");
// 转换为CGCS2000
foreach (var feature in layer.Features)
{
if (!string.IsNullOrEmpty(feature.Wkt))
{
feature.Wkt = CrsUtil.Transform(feature.Wkt, 4326, 4490);
}
}
layer.Wkid = 4490;
// 保存
OguLayerUtil.WriteLayer(DataFormatType.SHP, layer, "cgcs2000.shp");
6.11.2 字段筛选
// 读取原始数据
var srcLayer = OguLayerUtil.ReadLayer(DataFormatType.SHP, "source.shp");
// 创建只包含指定字段的新图层
var dstLayer = new OguLayer
{
Name = srcLayer.Name,
GeometryType = srcLayer.GeometryType,
Wkid = srcLayer.Wkid
};
// 只保留需要的字段
var keepFields = new[] { "ID", "Name", "Type" };
foreach (var fieldName in keepFields)
{
var field = srcLayer.GetField(fieldName);
if (field != null)
{
dstLayer.AddField(field.Clone());
}
}
// 复制要素,只保留指定字段的值
foreach (var srcFeature in srcLayer.Features)
{
var dstFeature = new OguFeature
{
Fid = srcFeature.Fid,
Wkt = srcFeature.Wkt
};
foreach (var fieldName in keepFields)
{
if (srcFeature.HasAttribute(fieldName))
{
dstFeature.SetValue(fieldName, srcFeature.GetValue(fieldName));
}
}
dstLayer.AddFeature(dstFeature);
}
// 保存
OguLayerUtil.WriteLayer(DataFormatType.GEOJSON, dstLayer, "filtered.geojson");
6.11.3 属性计算
var layer = OguLayerUtil.ReadLayer(DataFormatType.SHP, "polygons.shp");
// 添加面积字段
layer.AddField(new OguField
{
Name = "CalcArea",
DataType = FieldDataType.DOUBLE
});
// 计算面积
foreach (var feature in layer.Features)
{
if (!string.IsNullOrEmpty(feature.Wkt))
{
var area = GeometryUtil.AreaWkt(feature.Wkt);
feature.SetValue("CalcArea", area);
}
}
OguLayerUtil.WriteLayer(DataFormatType.SHP, layer, "with_area.shp");
6.12 小结
本章介绍了OGU4Net支持的各种数据格式转换:
- Shapefile:最常用格式,注意编码处理
- GeoJSON:Web友好,纯文本格式
- GeoPackage:OGC标准,单文件多图层
- FileGDB:Esri格式,需要特定驱动
- PostGIS:数据库存储,支持SQL查询
- KML:Google Earth格式
- DXF:CAD交换格式
- 国土TXT:中国自然资源部门格式
转换技巧:
- 使用OguLayerUtil简化转换操作
- 注意编码问题(尤其是Shapefile)
- 转换时可以进行坐标转换、字段筛选等处理
- 批量转换时考虑并行处理

浙公网安备 33010602011771号