雅安市网站建设_网站建设公司_页面权重_seo优化
2025/12/29 12:41:44 网站建设 项目流程

第09章:C#/.NET绑定开发指南

9.1 .NET GDAL 简介

9.1.1 概述

GDAL 为 .NET 平台提供了完整的绑定支持,允许 C# 开发者在 Windows 和跨平台环境中使用 GDAL 的全部功能。.NET GDAL 绑定特别适合企业级 Windows 应用开发。

┌─────────────────────────────────────────────────────────────┐
│                    .NET GDAL 架构                            │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│   ┌─────────────────────────────────────────────────────┐   │
│   │                .NET 应用层                           │   │
│   │    WPF/WinForms │ ASP.NET │ 控制台应用              │   │
│   └─────────────────────────────────────────────────────┘   │
│                           │                                  │
│                           ▼                                  │
│   ┌─────────────────────────────────────────────────────┐   │
│   │              GDAL .NET 绑定                          │   │
│   │    OSGeo.GDAL │ OSGeo.OGR │ OSGeo.OSR              │   │
│   └─────────────────────────────────────────────────────┘   │
│                           │                                  │
│                           ▼ P/Invoke                         │
│   ┌─────────────────────────────────────────────────────┐   │
│   │              GDAL 本地库 (C/C++)                     │   │
│   └─────────────────────────────────────────────────────┘   │
│                                                              │
└─────────────────────────────────────────────────────────────┘

9.1.2 可用的 NuGet 包

包名 描述 平台支持
MaxRev.Gdal.Universal 自动配置,跨平台支持 Windows, Linux, macOS
MaxRev.Gdal.WindowsRuntime.Minimal Windows 最小运行时 Windows
MaxRev.Gdal.LinuxRuntime.Minimal Linux 最小运行时 Linux
GDAL.Native 官方原生包 平台特定

9.2 环境配置

9.2.1 创建项目

# 创建新项目
dotnet new console -n GdalDemo -f net6.0
cd GdalDemo# 添加 NuGet 包
dotnet add package MaxRev.Gdal.Universal

9.2.2 项目文件配置

<!-- GdalDemo.csproj -->
<Project Sdk="Microsoft.NET.Sdk"><PropertyGroup><OutputType>Exe</OutputType><TargetFramework>net6.0</TargetFramework><ImplicitUsings>enable</ImplicitUsings><Nullable>enable</Nullable></PropertyGroup><ItemGroup><PackageReference Include="MaxRev.Gdal.Universal" Version="3.7.0.100" /></ItemGroup></Project>

9.2.3 初始化 GDAL

using MaxRev.Gdal.Core;
using OSGeo.GDAL;
using OSGeo.OGR;
using OSGeo.OSR;namespace GdalDemo;/// <summary>
/// GDAL 初始化帮助类
/// </summary>
public static class GdalInitializer
{private static bool _initialized = false;private static readonly object _lock = new object();/// <summary>/// 初始化 GDAL/// </summary>public static void Initialize(){if (_initialized) return;lock (_lock){if (_initialized) return;// 使用 MaxRev.Gdal 自动配置GdalBase.ConfigureAll();// 可选:设置配置选项Gdal.SetConfigOption("GDAL_FILENAME_IS_UTF8", "YES");Gdal.SetConfigOption("SHAPE_ENCODING", "UTF-8");// 打印版本信息Console.WriteLine($"GDAL 版本: {Gdal.VersionInfo("VERSION_NUM")}");Console.WriteLine($"栅格驱动数: {Gdal.GetDriverCount()}");Console.WriteLine($"矢量驱动数: {Ogr.GetDriverCount()}");_initialized = true;}}/// <summary>/// 列出所有驱动/// </summary>public static void ListDrivers(){Initialize();Console.WriteLine("\n栅格驱动:");for (int i = 0; i < Gdal.GetDriverCount(); i++){using var driver = Gdal.GetDriver(i);Console.WriteLine($"  {driver.ShortName}: {driver.LongName}");}Console.WriteLine("\n矢量驱动:");for (int i = 0; i < Ogr.GetDriverCount(); i++){using var driver = Ogr.GetDriver(i);Console.WriteLine($"  {driver.GetName()}");}}
}

9.3 栅格数据处理

9.3.1 读取栅格数据

using OSGeo.GDAL;
using OSGeo.OSR;namespace GdalDemo.Raster;/// <summary>
/// 栅格数据读取器
/// </summary>
public class RasterReader
{/// <summary>/// 读取栅格数据信息/// </summary>public static void ReadRasterInfo(string filepath){GdalInitializer.Initialize();using var ds = Gdal.Open(filepath, Access.GA_ReadOnly);if (ds == null){Console.WriteLine($"无法打开文件: {filepath}");return;}// 基本信息Console.WriteLine($"文件: {ds.GetDescription()}");Console.WriteLine($"驱动: {ds.GetDriver().ShortName}");Console.WriteLine($"尺寸: {ds.RasterXSize} x {ds.RasterYSize}");Console.WriteLine($"波段数: {ds.RasterCount}");// 地理变换double[] geoTransform = new double[6];ds.GetGeoTransform(geoTransform);Console.WriteLine($"原点: ({geoTransform[0]}, {geoTransform[3]})");Console.WriteLine($"像素大小: ({geoTransform[1]}, {geoTransform[5]})");// 投影信息string projection = ds.GetProjection();if (!string.IsNullOrEmpty(projection)){using var srs = new SpatialReference(projection);Console.WriteLine($"坐标系: {srs.GetName()}");}// 波段信息for (int i = 1; i <= ds.RasterCount; i++){using var band = ds.GetRasterBand(i);Console.WriteLine($"\n波段 {i}:");Console.WriteLine($"  数据类型: {Gdal.GetDataTypeName(band.DataType)}");double nodata;int hasNodata;band.GetNoDataValue(out nodata, out hasNodata);if (hasNodata != 0){Console.WriteLine($"  无效值: {nodata}");}double min, max, mean, stddev;band.GetStatistics(0, 1, out min, out max, out mean, out stddev);Console.WriteLine($"  统计: min={min:F2}, max={max:F2}, mean={mean:F2}, std={stddev:F2}");}}/// <summary>/// 读取波段数据为数组/// </summary>public static double[,] ReadBandAsArray(string filepath, int bandIndex = 1){GdalInitializer.Initialize();using var ds = Gdal.Open(filepath, Access.GA_ReadOnly);if (ds == null){throw new Exception($"无法打开文件: {filepath}");}int width = ds.RasterXSize;int height = ds.RasterYSize;using var band = ds.GetRasterBand(bandIndex);// 读取为一维数组double[] data = new double[width * height];band.ReadRaster(0, 0, width, height, data, width, height, 0, 0);// 转换为二维数组double[,] result = new double[height, width];for (int y = 0; y < height; y++){for (int x = 0; x < width; x++){result[y, x] = data[y * width + x];}}return result;}/// <summary>/// 分块读取栅格数据/// </summary>public static void ReadInBlocks(string filepath, Action<int, int, int, int, double[]> processor){GdalInitializer.Initialize();using var ds = Gdal.Open(filepath, Access.GA_ReadOnly);if (ds == null){throw new Exception($"无法打开文件: {filepath}");}int width = ds.RasterXSize;int height = ds.RasterYSize;using var band = ds.GetRasterBand(1);int blockWidth, blockHeight;band.GetBlockSize(out blockWidth, out blockHeight);Console.WriteLine($"块大小: {blockWidth} x {blockHeight}");for (int y = 0; y < height; y += blockHeight){for (int x = 0; x < width; x += blockWidth){int actualWidth = Math.Min(blockWidth, width - x);int actualHeight = Math.Min(blockHeight, height - y);double[] data = new double[actualWidth * actualHeight];band.ReadRaster(x, y, actualWidth, actualHeight, data, actualWidth, actualHeight, 0, 0);processor(x, y, actualWidth, actualHeight, data);}}}
}

9.3.2 创建栅格数据

using OSGeo.GDAL;
using OSGeo.OSR;namespace GdalDemo.Raster;/// <summary>
/// 栅格数据写入器
/// </summary>
public class RasterWriter
{/// <summary>/// 创建 GeoTIFF 文件/// </summary>public static void CreateGeoTiff(string filepath,int width,int height,int bands,DataType dataType,double[] geoTransform,int epsg,double[,]? data = null){GdalInitializer.Initialize();using var driver = Gdal.GetDriverByName("GTiff");if (driver == null){throw new Exception("GTiff 驱动不可用");}// 创建选项string[] options = new string[]{"COMPRESS=LZW","TILED=YES","BLOCKXSIZE=256","BLOCKYSIZE=256"};using var ds = driver.Create(filepath, width, height, bands, dataType, options);if (ds == null){throw new Exception($"无法创建文件: {filepath}");}// 设置地理变换if (geoTransform != null){ds.SetGeoTransform(geoTransform);}// 设置投影if (epsg > 0){using var srs = new SpatialReference("");srs.ImportFromEPSG(epsg);ds.SetProjection(srs.ExportToWkt());}// 写入数据if (data != null){using var band = ds.GetRasterBand(1);// 转换为一维数组double[] flatData = new double[width * height];for (int y = 0; y < height; y++){for (int x = 0; x < width; x++){flatData[y * width + x] = data[y, x];}}band.WriteRaster(0, 0, width, height, flatData, width, height, 0, 0);band.SetNoDataValue(-9999);band.ComputeStatistics(false, out _, out _, out _, out _, null, null);}ds.FlushCache();Console.WriteLine($"创建完成: {filepath}");}/// <summary>/// 创建示例 DEM/// </summary>public static void CreateSampleDEM(string filepath){int width = 1000;int height = 1000;// 生成模拟高程数据double[,] data = new double[height, width];Random rand = new Random();for (int y = 0; y < height; y++){for (int x = 0; x < width; x++){data[y, x] = 100 +50 * Math.Sin(x * Math.PI / 100) * Math.Cos(y * Math.PI / 100) +rand.NextDouble() * 10;}}// 地理变换double[] geoTransform = new double[]{116.0,   // 原点X0.001,   // 像素宽度0,       // 旋转40.0,    // 原点Y0,       // 旋转-0.001   // 像素高度};CreateGeoTiff(filepath, width, height, 1, DataType.GDT_Float32,geoTransform, 4326, data);}/// <summary>/// 创建 RGB 彩色影像/// </summary>public static void CreateRGBImage(string filepath,int width,int height,byte[,] red,byte[,] green,byte[,] blue,double[] geoTransform,int epsg){GdalInitializer.Initialize();using var driver = Gdal.GetDriverByName("GTiff");string[] options = new string[]{"COMPRESS=LZW","PHOTOMETRIC=RGB"};using var ds = driver.Create(filepath, width, height, 3, DataType.GDT_Byte, options);if (geoTransform != null){ds.SetGeoTransform(geoTransform);}if (epsg > 0){using var srs = new SpatialReference("");srs.ImportFromEPSG(epsg);ds.SetProjection(srs.ExportToWkt());}// 写入各波段byte[][] bandData = new byte[][] {FlattenArray(red),FlattenArray(green),FlattenArray(blue)};ColorInterp[] colorInterps = new ColorInterp[]{ColorInterp.GCI_RedBand,ColorInterp.GCI_GreenBand,ColorInterp.GCI_BlueBand};for (int i = 0; i < 3; i++){using var band = ds.GetRasterBand(i + 1);band.WriteRaster(0, 0, width, height, bandData[i], width, height, 0, 0);band.SetColorInterpretation(colorInterps[i]);}ds.FlushCache();}private static byte[] FlattenArray(byte[,] array){int height = array.GetLength(0);int width = array.GetLength(1);byte[] result = new byte[width * height];for (int y = 0; y < height; y++){for (int x = 0; x < width; x++){result[y * width + x] = array[y, x];}}return result;}
}

9.3.3 栅格处理操作

using OSGeo.GDAL;namespace GdalDemo.Raster;/// <summary>
/// 栅格处理操作
/// </summary>
public class RasterProcessor
{/// <summary>/// 重投影栅格/// </summary>public static void Reproject(string srcPath, string dstPath, string dstSRS){GdalInitializer.Initialize();using var srcDs = Gdal.Open(srcPath, Access.GA_ReadOnly);if (srcDs == null){throw new Exception($"无法打开文件: {srcPath}");}var options = new GDALWarpAppOptions(new string[]{"-t_srs", dstSRS,"-r", "bilinear","-co", "COMPRESS=LZW","-co", "TILED=YES"});using var dstDs = Gdal.Warp(dstPath, new Dataset[] { srcDs }, options, null, null);Console.WriteLine($"重投影完成: {dstPath}");}/// <summary>/// 裁剪栅格/// </summary>public static void Clip(string srcPath, string dstPath,double minX, double minY, double maxX, double maxY){GdalInitializer.Initialize();using var srcDs = Gdal.Open(srcPath, Access.GA_ReadOnly);var options = new GDALTranslateOptions(new string[]{"-projwin", minX.ToString(), maxY.ToString(), maxX.ToString(), minY.ToString(),"-co", "COMPRESS=LZW"});using var dstDs = Gdal.Translate(dstPath, srcDs, options, null, null);Console.WriteLine($"裁剪完成: {dstPath}");}/// <summary>/// 使用矢量边界裁剪/// </summary>public static void ClipByVector(string srcPath, string dstPath, string cutlinePath){GdalInitializer.Initialize();using var srcDs = Gdal.Open(srcPath, Access.GA_ReadOnly);var options = new GDALWarpAppOptions(new string[]{"-cutline", cutlinePath,"-crop_to_cutline","-dstnodata", "-9999","-co", "COMPRESS=LZW"});using var dstDs = Gdal.Warp(dstPath, new Dataset[] { srcDs }, options, null, null);Console.WriteLine($"裁剪完成: {dstPath}");}/// <summary>/// 镶嵌多个栅格/// </summary>public static void Mosaic(string[] srcPaths, string dstPath){GdalInitializer.Initialize();// 打开所有源数据集Dataset[] srcDatasets = new Dataset[srcPaths.Length];for (int i = 0; i < srcPaths.Length; i++){srcDatasets[i] = Gdal.Open(srcPaths[i], Access.GA_ReadOnly);if (srcDatasets[i] == null){throw new Exception($"无法打开文件: {srcPaths[i]}");}}try{// 创建 VRTvar vrtOptions = new GDALBuildVRTOptions(Array.Empty<string>());using var vrtDs = Gdal.BuildVRT("", srcDatasets, vrtOptions, null, null);// 转换为 GeoTIFFvar translateOptions = new GDALTranslateOptions(new string[]{"-co", "COMPRESS=LZW","-co", "TILED=YES","-co", "BIGTIFF=IF_SAFER"});using var dstDs = Gdal.Translate(dstPath, vrtDs, translateOptions, null, null);Console.WriteLine($"镶嵌完成: {dstPath}");}finally{foreach (var ds in srcDatasets){ds?.Dispose();}}}/// <summary>/// 格式转换/// </summary>public static void Convert(string srcPath, string dstPath, string format){GdalInitializer.Initialize();using var srcDs = Gdal.Open(srcPath, Access.GA_ReadOnly);var options = new GDALTranslateOptions(new string[]{"-of", format});using var dstDs = Gdal.Translate(dstPath, srcDs, options, null, null);Console.WriteLine($"转换完成: {dstPath}");}/// <summary>/// 创建金字塔/// </summary>public static void BuildOverviews(string filepath, int[] levels){GdalInitializer.Initialize();using var ds = Gdal.Open(filepath, Access.GA_Update);if (ds == null){throw new Exception($"无法打开文件: {filepath}");}if (levels == null || levels.Length == 0){levels = new int[] { 2, 4, 8, 16, 32 };}ds.BuildOverviews("AVERAGE", levels, null, null);Console.WriteLine($"金字塔创建完成: {filepath}");}
}

9.4 矢量数据处理

9.4.1 读取矢量数据

using OSGeo.OGR;
using OSGeo.OSR;namespace GdalDemo.Vector;/// <summary>
/// 矢量数据读取器
/// </summary>
public class VectorReader
{/// <summary>/// 读取矢量数据信息/// </summary>public static void ReadVectorInfo(string filepath){GdalInitializer.Initialize();using var ds = Ogr.Open(filepath, 0);if (ds == null){Console.WriteLine($"无法打开文件: {filepath}");return;}Console.WriteLine($"数据源: {ds.GetDescription()}");Console.WriteLine($"图层数: {ds.GetLayerCount()}");for (int i = 0; i < ds.GetLayerCount(); i++){using var layer = ds.GetLayerByIndex(i);Console.WriteLine($"\n图层 {i}: {layer.GetName()}");Console.WriteLine($"  要素数: {layer.GetFeatureCount(1)}");Console.WriteLine($"  几何类型: {Ogr.GeometryTypeToName(layer.GetGeomType())}");// 范围Envelope extent = new Envelope();layer.GetExtent(extent, 1);Console.WriteLine($"  范围: ({extent.MinX:F4}, {extent.MinY:F4}) - ({extent.MaxX:F4}, {extent.MaxY:F4})");// 空间参考using var srs = layer.GetSpatialRef();if (srs != null){Console.WriteLine($"  坐标系: {srs.GetName()}");}// 字段定义using var layerDefn = layer.GetLayerDefn();Console.WriteLine($"  字段数: {layerDefn.GetFieldCount()}");for (int j = 0; j < layerDefn.GetFieldCount(); j++){using var fieldDefn = layerDefn.GetFieldDefn(j);Console.WriteLine($"    - {fieldDefn.GetName()}: {fieldDefn.GetTypeName()}({fieldDefn.GetWidth()})");}}}/// <summary>/// 遍历要素/// </summary>public static void IterateFeatures(string filepath, Action<Feature>? processor = null){GdalInitializer.Initialize();using var ds = Ogr.Open(filepath, 0);if (ds == null){throw new Exception($"无法打开文件: {filepath}");}using var layer = ds.GetLayerByIndex(0);layer.ResetReading();Feature? feature;while ((feature = layer.GetNextFeature()) != null){using (feature){if (processor != null){processor(feature);}else{Console.WriteLine($"FID: {feature.GetFID()}");using var geom = feature.GetGeometryRef();if (geom != null){Console.WriteLine($"  几何类型: {geom.GetGeometryName()}");Console.WriteLine($"  WKT: {geom.ExportToWkt().Substring(0, Math.Min(50, geom.ExportToWkt().Length))}...");}}}}}/// <summary>/// 读取要素为字典列表/// </summary>public static List<Dictionary<string, object?>> ReadFeatures(string filepath,string? whereClause = null,(double MinX, double MinY, double MaxX, double MaxY)? bbox = null){GdalInitializer.Initialize();using var ds = Ogr.Open(filepath, 0);if (ds == null){throw new Exception($"无法打开文件: {filepath}");}using var layer = ds.GetLayerByIndex(0);// 设置过滤器if (!string.IsNullOrEmpty(whereClause)){layer.SetAttributeFilter(whereClause);}if (bbox.HasValue){layer.SetSpatialFilterRect(bbox.Value.MinX, bbox.Value.MinY,bbox.Value.MaxX, bbox.Value.MaxY);}var features = new List<Dictionary<string, object?>>();using var layerDefn = layer.GetLayerDefn();Feature? feature;layer.ResetReading();while ((feature = layer.GetNextFeature()) != null){using (feature){var dict = new Dictionary<string, object?>{["fid"] = feature.GetFID()};using var geom = feature.GetGeometryRef();if (geom != null){dict["geometry"] = geom.ExportToWkt();}for (int i = 0; i < layerDefn.GetFieldCount(); i++){using var fieldDefn = layerDefn.GetFieldDefn(i);string fieldName = fieldDefn.GetName();if (feature.IsFieldNull(i)){dict[fieldName] = null;}else{dict[fieldName] = fieldDefn.GetFieldType() switch{FieldType.OFTInteger => feature.GetFieldAsInteger(i),FieldType.OFTInteger64 => feature.GetFieldAsInteger64(i),FieldType.OFTReal => feature.GetFieldAsDouble(i),_ => feature.GetFieldAsString(i)};}}features.Add(dict);}}return features;}
}

9.4.2 创建矢量数据

using OSGeo.OGR;
using OSGeo.OSR;namespace GdalDemo.Vector;/// <summary>
/// 矢量数据写入器
/// </summary>
public class VectorWriter
{/// <summary>/// 创建 Shapefile/// </summary>public static void CreateShapefile(string filepath,wkbGeometryType geomType,int epsg,List<(string Name, FieldType Type, int Width)> fields,List<Dictionary<string, object?>> features){GdalInitializer.Initialize();using var driver = Ogr.GetDriverByName("ESRI Shapefile");if (driver == null){throw new Exception("Shapefile 驱动不可用");}// 删除已存在的文件if (File.Exists(filepath)){driver.DeleteDataSource(filepath);}using var ds = driver.CreateDataSource(filepath, Array.Empty<string>());if (ds == null){throw new Exception($"无法创建数据源: {filepath}");}// 创建空间参考using var srs = new SpatialReference("");srs.ImportFromEPSG(epsg);// 创建图层using var layer = ds.CreateLayer("layer", srs, geomType, Array.Empty<string>());// 创建字段foreach (var (name, type, width) in fields){using var fieldDefn = new FieldDefn(name, type);if (width > 0){fieldDefn.SetWidth(width);}layer.CreateField(fieldDefn, 1);}// 获取图层定义using var layerDefn = layer.GetLayerDefn();// 添加要素foreach (var featureData in features){using var feature = new Feature(layerDefn);// 设置几何if (featureData.TryGetValue("geometry", out var geomObj) && geomObj is string wkt){using var geom = Geometry.CreateFromWkt(wkt);feature.SetGeometry(geom);}// 设置属性foreach (var (name, _, _) in fields){if (featureData.TryGetValue(name, out var value) && value != null){int fieldIndex = feature.GetFieldIndex(name);if (fieldIndex >= 0){switch (value){case int intVal:feature.SetField(fieldIndex, intVal);break;case long longVal:feature.SetField(fieldIndex, longVal);break;case double doubleVal:feature.SetField(fieldIndex, doubleVal);break;default:feature.SetField(fieldIndex, value.ToString());break;}}}}layer.CreateFeature(feature);}Console.WriteLine($"创建完成: {filepath}");}/// <summary>/// 创建 GeoJSON/// </summary>public static void CreateGeoJSON(string filepath,wkbGeometryType geomType,int epsg,List<(string Name, FieldType Type, int Width)> fields,List<Dictionary<string, object?>> features){GdalInitializer.Initialize();using var driver = Ogr.GetDriverByName("GeoJSON");if (driver == null){throw new Exception("GeoJSON 驱动不可用");}using var ds = driver.CreateDataSource(filepath, Array.Empty<string>());using var srs = new SpatialReference("");srs.ImportFromEPSG(epsg);using var layer = ds.CreateLayer("features", srs, geomType, Array.Empty<string>());foreach (var (name, type, width) in fields){using var fieldDefn = new FieldDefn(name, type);if (width > 0){fieldDefn.SetWidth(width);}layer.CreateField(fieldDefn, 1);}using var layerDefn = layer.GetLayerDefn();foreach (var featureData in features){using var feature = new Feature(layerDefn);if (featureData.TryGetValue("geometry", out var geomObj) && geomObj is string wkt){using var geom = Geometry.CreateFromWkt(wkt);feature.SetGeometry(geom);}foreach (var (name, _, _) in fields){if (featureData.TryGetValue(name, out var value) && value != null){int fieldIndex = feature.GetFieldIndex(name);if (fieldIndex >= 0){feature.SetField(fieldIndex, value.ToString());}}}layer.CreateFeature(feature);}Console.WriteLine($"创建完成: {filepath}");}/// <summary>/// 创建示例点数据/// </summary>public static void CreateSamplePoints(string filepath){var fields = new List<(string Name, FieldType Type, int Width)>{("name", FieldType.OFTString, 50),("population", FieldType.OFTInteger64, 0)};var features = new List<Dictionary<string, object?>>{new() { ["geometry"] = "POINT(116.4 39.9)", ["name"] = "北京", ["population"] = 21540000L },new() { ["geometry"] = "POINT(121.5 31.2)", ["name"] = "上海", ["population"] = 24280000L },new() { ["geometry"] = "POINT(113.3 23.1)", ["name"] = "广州", ["population"] = 18680000L }};CreateShapefile(filepath, wkbGeometryType.wkbPoint, 4326, fields, features);}
}

9.5 坐标转换

using OSGeo.OGR;
using OSGeo.OSR;namespace GdalDemo.Coordinate;/// <summary>
/// 坐标转换工具
/// </summary>
public class CoordinateTransformer
{/// <summary>/// 转换坐标点/// </summary>public static (double X, double Y) TransformPoint(double x, double y,int srcEPSG, int dstEPSG){GdalInitializer.Initialize();using var srcSRS = new SpatialReference("");srcSRS.ImportFromEPSG(srcEPSG);using var dstSRS = new SpatialReference("");dstSRS.ImportFromEPSG(dstEPSG);using var transform = new CoordinateTransformation(srcSRS, dstSRS);double[] point = new double[] { x, y, 0 };transform.TransformPoint(point);return (point[0], point[1]);}/// <summary>/// 批量转换坐标/// </summary>public static List<(double X, double Y)> TransformPoints(List<(double X, double Y)> points,int srcEPSG, int dstEPSG){GdalInitializer.Initialize();using var srcSRS = new SpatialReference("");srcSRS.ImportFromEPSG(srcEPSG);using var dstSRS = new SpatialReference("");dstSRS.ImportFromEPSG(dstEPSG);using var transform = new CoordinateTransformation(srcSRS, dstSRS);var results = new List<(double X, double Y)>();foreach (var (x, y) in points){double[] point = new double[] { x, y, 0 };transform.TransformPoint(point);results.Add((point[0], point[1]));}return results;}/// <summary>/// 转换几何对象/// </summary>public static Geometry TransformGeometry(Geometry geom,int srcEPSG, int dstEPSG){GdalInitializer.Initialize();using var srcSRS = new SpatialReference("");srcSRS.ImportFromEPSG(srcEPSG);using var dstSRS = new SpatialReference("");dstSRS.ImportFromEPSG(dstEPSG);using var transform = new CoordinateTransformation(srcSRS, dstSRS);var newGeom = geom.Clone();newGeom.Transform(transform);return newGeom;}
}

9.6 实用工具类

using OSGeo.GDAL;
using OSGeo.OGR;namespace GdalDemo.Utils;/// <summary>
/// GDAL 资源包装器
/// </summary>
public class GdalResource : IDisposable
{private Dataset? _rasterDs;private DataSource? _vectorDs;private bool _disposed = false;public Dataset? RasterDataset => _rasterDs;public DataSource? VectorDataSource => _vectorDs;private GdalResource() { }public static GdalResource OpenRaster(string filepath, Access access = Access.GA_ReadOnly){GdalInitializer.Initialize();var resource = new GdalResource();resource._rasterDs = Gdal.Open(filepath, access);if (resource._rasterDs == null){throw new Exception($"无法打开栅格文件: {filepath}");}return resource;}public static GdalResource OpenVector(string filepath, int update = 0){GdalInitializer.Initialize();var resource = new GdalResource();resource._vectorDs = Ogr.Open(filepath, update);if (resource._vectorDs == null){throw new Exception($"无法打开矢量文件: {filepath}");}return resource;}public void Dispose(){Dispose(true);GC.SuppressFinalize(this);}protected virtual void Dispose(bool disposing){if (!_disposed){if (disposing){_rasterDs?.Dispose();_vectorDs?.Dispose();}_rasterDs = null;_vectorDs = null;_disposed = true;}}~GdalResource(){Dispose(false);}
}

9.7 主程序示例

using GdalDemo;
using GdalDemo.Raster;
using GdalDemo.Vector;
using GdalDemo.Coordinate;// 初始化 GDAL
GdalInitializer.Initialize();Console.WriteLine("=== GDAL .NET 示例 ===\n");// 1. 创建示例 DEM
Console.WriteLine("1. 创建示例 DEM...");
RasterWriter.CreateSampleDEM("sample_dem.tif");// 2. 读取栅格信息
Console.WriteLine("\n2. 读取栅格信息...");
RasterReader.ReadRasterInfo("sample_dem.tif");// 3. 创建示例点数据
Console.WriteLine("\n3. 创建示例点数据...");
VectorWriter.CreateSamplePoints("cities.shp");// 4. 读取矢量信息
Console.WriteLine("\n4. 读取矢量信息...");
VectorReader.ReadVectorInfo("cities.shp");// 5. 坐标转换
Console.WriteLine("\n5. 坐标转换...");
var (x, y) = CoordinateTransformer.TransformPoint(116.4, 39.9, 4326, 3857);
Console.WriteLine($"WGS84 (116.4, 39.9) -> Web Mercator ({x:F2}, {y:F2})");Console.WriteLine("\n=== 完成 ===");

9.8 本章小结

本章介绍了 C#/.NET GDAL 开发:

  1. 环境配置:使用 MaxRev.Gdal.Universal NuGet 包
  2. 栅格处理:读取、创建和处理栅格数据
  3. 矢量处理:读取、创建和查询矢量数据
  4. 坐标转换:使用 OSR 进行坐标转换
  5. 最佳实践:资源管理和 IDisposable 模式

9.9 思考与练习

  1. 比较 MaxRev.Gdal.Universal 和其他 GDAL NuGet 包的优缺点。
  2. 如何在 ASP.NET Core Web API 中使用 GDAL?
  3. 实现一个支持异步操作的栅格处理类。
  4. 编写一个批量格式转换工具。
  5. 如何处理 GDAL 中的大文件和内存管理?

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询