我设法写了一个'for dummies'如何计算C#中不规则多边形的面积,但我需要它对任意数量的顶点都是动态的。
有人可以帮忙吗?
类别:
public class Vertex
{
private int _vertexIdx;
private double _coordX;
private double _coordY;
private double _coordZ;
public Vertex()
{ }
public Vertex(int vertexIdx, double coordX, double coordY, double coordZ)
{
_vertexIdx = vertexIdx;
_coordX = coordX;
_coordY = coordY;
_coordZ = coordZ;
}
public int VertexIdx
{
get { return _vertexIdx; }
set { _vertexIdx = value; }
}
public double X
{
get { return _coordX; }
set { _coordX = value; }
}
public double Y
{
get { return _coordY; }
set { _coordY = value; }
}
public double Z
{
get { return _coordZ; }
set { _coordZ = value; }
}
}
的Form_Load:
List<Vertex> verticies = new List<Vertex>();
verticies.Add(new Vertex(1, 930.9729, 802.8789, 0));
verticies.Add(new Vertex(2, 941.5341, 805.662, 0));
verticies.Add(new Vertex(3, 946.5828, 799.271, 0));
verticies.Add(new Vertex(4, 932.6215, 797.0548, 0));
dataGridView1.DataSource = verticies;
计算何时按下按钮的代码:(硬编码为4点多边形 - 应该是任何数量......)
// X-coords
double x1;
double x2;
double x3;
double x4;
double x5;
// Y-coords
double y1;
double y2;
double y3;
double y4;
double y5;
// Xn * Yn++
double x1y2;
double x2y3;
double x3y4;
double x4y5;
// Yn * Xn++
double y1x2;
double y2x3;
double y3x4;
double y4x5;
// XnYn++ - YnXn++
double x1y2my1x2;
double x2y3my2x3;
double x3y4my3x4;
double x4y5my4x5;
double result;
double area;
x1 = Convert.ToDouble(dataGridView1.Rows[0].Cells[1].Value.ToString());
y1 = Convert.ToDouble(dataGridView1.Rows[0].Cells[2].Value.ToString());
txtLog.Text += String.Format("X1 = {0}\tY1 = {1}\r\n", x1, y1);
x2 = Convert.ToDouble(dataGridView1.Rows[1].Cells[1].Value.ToString());
y2 = Convert.ToDouble(dataGridView1.Rows[1].Cells[2].Value.ToString());
txtLog.Text += String.Format("X2 = {0}\tY2 = {1}\r\n", x2, y2);
x3 = Convert.ToDouble(dataGridView1.Rows[2].Cells[1].Value.ToString());
y3 = Convert.ToDouble(dataGridView1.Rows[2].Cells[2].Value.ToString());
txtLog.Text += String.Format("X3 = {0}\tY3 = {1}\r\n", x3, y3);
x4 = Convert.ToDouble(dataGridView1.Rows[3].Cells[1].Value.ToString());
y4 = Convert.ToDouble(dataGridView1.Rows[3].Cells[2].Value.ToString());
txtLog.Text += String.Format("X4 = {0}\tY4 = {1}\r\n", x4, y4);
// add the start point again
x5 = Convert.ToDouble(dataGridView1.Rows[0].Cells[1].Value.ToString());
y5 = Convert.ToDouble(dataGridView1.Rows[0].Cells[2].Value.ToString());
txtLog.Text += String.Format("X5 = {0}\tY5 = {1}\r\n", x5, y5);
txtLog.Text += "\r\n";
// Multiply
x1y2 = x1 * y2;
x2y3 = x2 * y3;
x3y4 = x3 * y4;
x4y5 = x4 * y5;
y1x2 = y1 * x2;
y2x3 = y2 * x3;
y3x4 = y3 * x4;
y4x5 = y4 * x5;
// Subtract from each other
x1y2my1x2 = x1y2 - y1x2;
x2y3my2x3 = x2y3 - y2x3;
x3y4my3x4 = x3y4 - y3x4;
x4y5my4x5 = x4y5 - y4x5;
// Sum all results
result = x1y2my1x2 + x2y3my2x3 + x3y4my3x4 + x4y5my4x5;
area = Math.Abs(result / 2);
txtLog.Text += String.Format("Area = {0}\r\n", area);
示例输出:
X1 = 930.9729 Y1 = 802.8789
X2 = 941.5341 Y2 = 805.662
X3 = 946.5828 Y3 = 799.271
X4 = 932.6215 Y4 = 797.0548
X5 = 930.9729 Y5 = 802.8789
面积= 83.2566504099523
答案 0 :(得分:15)
使用lambda表达式,这变得微不足道了!
var points = GetSomePoints();
points.Add(points[0]);
var area = Math.Abs(points.Take(points.Count - 1)
.Select((p, i) => (points[i + 1].X - p.X) * (points[i + 1].Y + p.Y))
.Sum() / 2);
该算法解释为here:
[此方法添加]多边形边缘定义的梯形区域掉落 到X轴。当程序考虑多边形的底边时, 计算给出负面积所以多边形之间的空间 并减去轴,留下多边形的区域。
如果多边形顺时针方向,则总计算面积为负[所以]函数只返回绝对值。
这种方法 对非简单多边形(边交叉)给出奇怪的结果。
答案 1 :(得分:5)
public float Area(List<PointF> vertices)
{
vertices.Add(vertices[0]);
return Math.Abs(vertices.Take(vertices.Count - 1).Select((p, i) => (p.X * vertices[i + 1].Y) - (p.Y * vertices[i + 1].X)).Sum() / 2);
}
答案 2 :(得分:3)
像plain polygon那样的东西(由记事本编译):
static double GetDeterminant(double x1, double y1, double x2, double y2)
{
return x1 * y2 - x2 * y1;
}
static double GetArea(IList<Vertex> vertices)
{
if(vertices.Count < 3)
{
return 0;
}
double area = GetDeterminant(vertices[vertices.Count - 1].X, vertices[vertices.Count - 1].Y, vertices[0].X, vertices[0].Y);
for (int i = 1; i < vertices.Count; i++)
{
area += GetDeterminant(vertices[i - 1].X, vertices[i - 1].Y, vertices[i].X, vertices[i].Y);
}
return area / 2;
}
虽然你的方法不注意Z轴。因此,我建议应用一些转换来摆脱它:如果多边形不是平面,你将无法获得区域,而如果它是平面,你就可以摆脱第三维。
答案 3 :(得分:0)
double resultant = 0;
double area = 0;
int tel1 = 0;
int tel2 = 0;
x1y2lst.Clear();
y1x2lst.Clear();
x1y2lstMinusy1x2lst.Clear();
// *******************************************************************************************//
// Calculate and populate X1 * Y2 in a list
for (int i = 0; i < dataGridView1.Rows.Count - 1; i++)
{
tel1++;
double x1x = Convert.ToDouble(dataGridView1.Rows[i].Cells[1].Value.ToString());
double y2y = Convert.ToDouble(dataGridView1.Rows[i+1].Cells[2].Value.ToString());
x1y2lst.Add(x1x * y2y);
}
// Calculate the last with the first value
double xLastx = Convert.ToDouble(dataGridView1.Rows[tel1].Cells[1].Value.ToString());
double yFirsty = Convert.ToDouble(dataGridView1.Rows[0].Cells[2].Value.ToString());
x1y2lst.Add(xLastx * yFirsty);
// *******************************************************************************************//
// Calculate and populate Y1 * X2 in a list
for (int i = 0; i < dataGridView1.Rows.Count - 1; i++)
{
tel2++;
double y1y = Convert.ToDouble(dataGridView1.Rows[i].Cells[2].Value.ToString());
double x2x = Convert.ToDouble(dataGridView1.Rows[i + 1].Cells[1].Value.ToString());
y1x2lst.Add(y1y * x2x);
}
// Calculate the last with the first value
double yLasty = Convert.ToDouble(dataGridView1.Rows[tel2].Cells[2].Value.ToString());
double xFirstx = Convert.ToDouble(dataGridView1.Rows[0].Cells[1].Value.ToString());
y1x2lst.Add(yLasty * xFirstx);
// Subract List1 values from List2 values
for (int k = 0; k < x1y2lst.Count; k++)
{
x1y2lstMinusy1x2lst.Add(x1y2lst[k] - y1x2lst[k]);
}
// Add all answers from previous to a result
for (int l = 0; l < x1y2lstMinusy1x2lst.Count; l++)
{
resultant += x1y2lstMinusy1x2lst[l];
}
// Area = Result from steps above devided by 2
area = Math.Abs(resultant / 2);
txtArea.Text = Math.Round(area, 4).ToString();
答案 4 :(得分:0)
我发现在计算大约 600,000 个多边形的面积时,上面显示的基本公式适用于某些多边形,但很多都在很大程度上超出了范围。我正在根据 https://geojson.io/ 抽查我的结果 - 对于非常复杂的带有孔洞的多边形(例如中间的湖泊),它返回了正确的结果。为了计算复杂多边形的正确面积,我最终使用了 geojson.io 使用的相同系统 - 客户端 js 库 Turf.js 请参见此处 https://turfjs.org/docs/#area
在这张图片中,您可以看到我的第一次尝试,然后是我使用 Turf.js 的第二次尝试 - 那里有一列显示第一次尝试与第二次尝试相比的正确率,其中 1 是相同的计算。您可以看到它们大多接近,但有些相差 10 倍或更差。
这是一些用于计算的示例代码。我让它加载 200 到屏幕上,然后在 JS 中运行计算,并将结果 ajax 返回到服务器端数据库。
$(document).ready(function () {
var items = $('.geojson');
for (var sc = 0; sc < items.length; sc++) {
var id = $(items[sc]).attr('data-id');
var polyData = JSON.parse($(items[sc]).find('.geojson-data').html());
//console.log('[' + polyData + ']');
var polygon = turf.polygon(polyData.coordinates);
var area = turf.area(polygon);
console.log('id[' + id + ']sqm[' + area + ']');
$(items[sc]).closest('tr').find('.data-id').html(id);
var answerDom = $(items[sc]).closest('tr').find('.answer');
if (true) {
$.ajax({
url: 'calc-poly-area-from-geojson-turf-scheduled.aspx',
type: "get",
data: {id:id, area: area },
invokedata: { answerDom: answerDom, area: area },
async: true,//run all at the same time
cache: false,
success: function (data) {
//console.log('test email done');
$(this.invokedata.answerDom).html('ok:' + this.invokedata.area);
$(this.invokedata.answerDom).removeClass('answer');
if ($('.answer').length == 0) {
window.location.reload();
}
},
error: function (msg) {
console.log("call failed: " + msg.responseText);
$(el).html('error in call');
//prompt('copy this',url+'?'+qs)
}
});
}
}
});
window.setTimeout(function () { window.location.reload(); }, 10000);
示例多边形在这里
{"type":"Polygon", "coordinates":[[[171.519147876006,-43.809111826162],[171.519264282931,-43.8094307100015],[171.519615782201,-43.8097268361192],[171.519874096036,-43.8097860548424],[171.525264107563,-43.8176887926426],[171.525356625489,-43.8179845471556],[171.525750029905,-43.8185636705947],[171.526002901974,-43.8187934292356],[171.526154917292,-43.8189686576417],[171.526249645477,-43.8191111884506],[171.526245660987,-43.819269203656],[171.526032299227,-43.8200263808647],[171.524134038501,-43.8268225827224],[171.523301803308,-43.8297987275054],[171.523129147529,-43.8301621243769],[171.522991616155,-43.8300725313285],[171.52248605771,-43.8302181414427],[171.522128893843,-43.8304084928376],[171.521558488905,-43.8304389785399],[171.521371202269,-43.830481916342],[171.521023295734,-43.8309120441211],[171.520774217465,-43.8310054055632],[171.520589483523,-43.8311387384524],[171.515210823266,-43.8294163992962],[171.514763136723,-43.8292736695248],[171.496256757791,-43.8233680542711],[171.494338310605,-43.8227558913632],[171.493450128779,-43.8224739752289],[171.493221517911,-43.8223838125259],[171.493001278557,-43.8222877021167],[171.492654147639,-43.821801588707],[171.491048512765,-43.8200169686591],[171.488157604579,-43.8168246695455],[171.488051808197,-43.8166695752984],[171.487648717141,-43.8162207994268],[171.486147094889,-43.8145461538075],[171.482241975825,-43.8101769774879],[171.481683765874,-43.8095751045999],[171.480858016595,-43.8085443728491],[171.481124633337,-43.8086557677844],[171.481334008334,-43.8085534985925],[171.481540735171,-43.8083379086683],[171.4815994175,-43.8077828104991],[171.481763314624,-43.8074471226617],[171.481812168914,-43.8064706917151],[171.48196041271,-43.8063093336607],[171.482260412185,-43.8062322290662],[171.482916004007,-43.8059780008537],[171.494844864468,-43.8013540958407],[171.501308718774,-43.7988446798756],[171.506019390319,-43.797017657826],[171.508275460952,-43.7961421972998],[171.508430707528,-43.7960805551645],[171.509117292333,-43.7963432108869],[171.510511038963,-43.7968679021071],[171.513299102675,-43.8007637699317],[171.513465917258,-43.8010892007185],[171.513696634335,-43.8013818859084],[171.513929550742,-43.8016136793445],[171.514114411714,-43.8018826827151],[171.514305634465,-43.8021912982997],[171.51440028511,-43.8024789426394],[171.514828618996,-43.8028429251794],[171.51494106207,-43.8031623582355],[171.515852739466,-43.8044825303059],[171.516111930457,-43.8047763591375],[171.517116748697,-43.8062534995253],[171.517374596163,-43.8065473602078],[171.517549793874,-43.8068229401963],[171.5176213721,-43.8070824951625],[171.517796573697,-43.8073580748019],[171.518070610117,-43.8076087983324],[171.518880109148,-43.8088563353488],[171.519147876006,-43.809111826162]]]}
您可以将其粘贴到 geojson.io 中以检查它们的区域(单击 poly,然后单击“属性”选项卡)