已知一个Polygon用c#计算其质心
在 C# 中计算多边形质心有多种方法,从使用成熟的几何库到手动实现算法。以下是几种常用方案:
📐 方法一:使用 NetTopologySuite(推荐)
NetTopologySuite (NTS) 是 .NET 中最强大的几何库,提供了完善的质心计算。
1. 安装 NuGet 包
dotnet add package NetTopologySuite
2. 计算质心
using NetTopologySuite.Geometries;
using NetTopologySuite.IO;
using System;
public class CentroidCalculator
{
/// <summary>
/// 从 WKT 字符串计算质心
/// </summary>
public static Coordinate CalculateCentroidFromWkt(string wkt)
{
var reader = new WKTReader();
var geometry = reader.Read(wkt);
// 计算质心
var centroid = geometry.Centroid;
return centroid.Coordinate;
}
/// <summary>
/// 从坐标点集合计算质心
/// </summary>
public static Coordinate CalculateCentroidFromPoints(Coordinate[] points)
{
var geometryFactory = new GeometryFactory();
var polygon = geometryFactory.CreatePolygon(points);
var centroid = polygon.Centroid;
return centroid.Coordinate;
}
}
// 使用示例
string wkt = "POLYGON((0 0,0 23.622,23.622 23.622,23.622 0,0 0))";
var centroid = CentroidCalculator.CalculateCentroidFromWkt(wkt);
Console.WriteLine($"质心坐标: ({centroid.X:F3}, {centroid.Y:F3})");
📐 方法二:手动实现算法
1. 数学原理
对于简单多边形,质心计算公式:
带符号面积 A = 0.5 * Σ (xi*yi+1 - xi+1*yi)
Cx = (1/(6A)) * Σ (xi + xi+1)*(xi*yi+1 - xi+1*yi)
Cy = (1/(6A)) * Σ (yi + yi+1)*(xi*yi+1 - xi+1*yi)
2. 完整实现
using System;
using System.Collections.Generic;
using System.Windows; // 或自定义 Point 结构
public class ManualCentroidCalculator
{
/// <summary>
/// 计算多边形质心(简单多边形,无孔洞)
/// </summary>
public static Point CalculateCentroid(IList<Point> polygon)
{
if (polygon == null || polygon.Count < 3)
throw new ArgumentException("多边形至少需要3个点");
double signedArea = 0.0;
double centroidX = 0.0;
double centroidY = 0.0;
int count = polygon.Count;
for (int i = 0; i < count; i++)
{
int j = (i + 1) % count;
double xi = polygon[i].X;
double yi = polygon[i].Y;
double xj = polygon[j].X;
double yj = polygon[j].Y;
double crossProduct = xi * yj - xj * yi;
signedArea += crossProduct;
centroidX += (xi + xj) * crossProduct;
centroidY += (yi + yj) * crossProduct;
}
signedArea *= 0.5;
double factor = 1.0 / (6.0 * signedArea);
centroidX *= factor;
centroidY *= factor;
return new Point(centroidX, centroidY);
}
/// <summary>
/// 从 WKT 字符串解析多边形并计算质心
/// </summary>
public static Point CalculateCentroidFromWkt(string wkt)
{
var points = ParseWktPolygon(wkt);
return CalculateCentroid(points);
}
/// <summary>
/// 解析 WKT 格式的多边形字符串
/// </summary>
private static List<Point> ParseWktPolygon(string wkt)
{
// 简单解析 POLYGON((x1 y1, x2 y2, ...))
var points = new List<Point>();
// 提取坐标部分
int start = wkt.IndexOf("((") + 2;
int end = wkt.IndexOf("))");
string coordsStr = wkt.Substring(start, end - start);
// 分割坐标对
string[] coordPairs = coordsStr.Split(',');
foreach (var pair in coordPairs)
{
string trimmed = pair.Trim();
string[] xy = trimmed.Split(' ');
if (xy.Length == 2 &&
double.TryParse(xy[0], out double x) &&
double.TryParse(xy[1], out double y))
{
points.Add(new Point(x, y));
}
}
return points;
}
}
// 使用示例
var polygon = new List<Point>
{
new Point(0, 0),
new Point(0, 23.622),
new Point(23.622, 23.622),
new Point(23.622, 0),
new Point(0, 0) // 闭合点
};
var centroid = ManualCentroidCalculator.CalculateCentroid(polygon);
Console.WriteLine($"质心坐标: ({centroid.X:F3}, {centroid.Y:F3})");
// 或直接从 WKT 计算
string wkt = "POLYGON((0 0,0 23.622,23.622 23.622,23.622 0,0 0))";
centroid = ManualCentroidCalculator.CalculateCentroidFromWkt(wkt);
Console.WriteLine($"从WKT计算的质心: ({centroid.X:F3}, {centroid.Y:F3})");
📐 方法三:使用 System.Drawing(适用于 Windows 窗体)
using System.Drawing;
using System.Drawing.Drawing2D;
public class DrawingCentroidCalculator
{
public static PointF CalculateCentroid(PointF[] polygon)
{
if (polygon.Length < 3)
throw new ArgumentException("多边形至少需要3个点");
using (var path = new GraphicsPath())
{
path.AddPolygon(polygon);
var region = new Region(path);
var bounds = region.GetBounds();
// 注意:这只是边界框中心,不是几何质心
// 对于精确质心,仍需使用数学公式
return new PointF(
bounds.X + bounds.Width / 2,
bounds.Y + bounds.Height / 2
);
}
}
}
📊 与 MySQL 结果对比验证
using MySql.Data.MySqlClient;
using NetTopologySuite.Geometries;
using NetTopologySuite.IO;
public class DatabaseCentroidValidator
{
public static void ValidateWithDatabase()
{
string connectionString = "Server=localhost;Database=cdb;Uid=root;Pwd=ketmaC;";
using var connection = new MySqlConnection(connectionString);
connection.Open();
string query = @"
SELECT
id,
SpatialID,
ST_AsText(Profile) as profile_wkt,
ST_X(ST_Centroid(Profile)) as mysql_centroid_x,
ST_Y(ST_Centroid(Profile)) as mysql_centroid_y
FROM steps_instances
LIMIT 10";
using var command = new MySqlCommand(query, connection);
using var reader = command.ExecuteReader();
var wktReader = new WKTReader();
while (reader.Read())
{
int id = reader.GetInt32("id");
string spatialId = reader.GetString("SpatialID");
string wkt = reader.GetString("profile_wkt");
double mysqlCentroidX = reader.GetDouble("mysql_centroid_x");
double mysqlCentroidY = reader.GetDouble("mysql_centroid_y");
// 使用 NTS 计算质心
var geometry = wktReader.Read(wkt);
var centroid = geometry.Centroid.Coordinate;
// 对比结果
Console.WriteLine($"ID: {id}");
Console.WriteLine($"SpatialID: {spatialId}");
Console.WriteLine($"MySQL 质心: ({mysqlCentroidX:F6}, {mysqlCentroidY:F6})");
Console.WriteLine($"C# 质心: ({centroid.X:F6}, {centroid.Y:F6})");
Console.WriteLine($"差异: X={Math.Abs(mysqlCentroidX - centroid.X):F6}, Y={Math.Abs(mysqlCentroidY - centroid.Y):F6}");
Console.WriteLine("---");
}
}
}
💡 选择建议
| 方法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| NetTopologySuite | 功能完整,支持复杂几何 | 需要安装包 | 生产环境、复杂几何处理 |
| 手动实现 | 无依赖,代码透明 | 需要处理边缘情况 | 学习原理、简单多边形 |
| System.Drawing | .NET 内置 | 计算不精确 | 简单图形处理 |
🔧 性能优化技巧
// 1. 使用 Span<T> 减少内存分配
public static Point CalculateCentroidSpan(Span<Point> polygon)
{
// 实现类似,但使用 Span 避免数组分配
}
// 2. 并行计算多个多边形的质心
var centroids = polygons.AsParallel()
.Select(p => CalculateCentroid(p))
.ToArray();
// 3. 缓存几何对象
private static readonly GeometryFactory _geometryFactory = new GeometryFactory();
推荐使用 NetTopologySuite,因为它经过充分测试,支持各种几何类型,并且与数据库中的空间数据格式兼容。
评论 0
发表评论
暂无评论,快来发表第一条评论吧!