11
votes

Zone de calcul du polygone irrégulier en C #

J'ai réussi à écrire un "pour les nuls" comment calculer la zone de polygone irrégulier en C #, mais j'en ai besoin pour être dynamique pour toute quantité de verticie forte>.

Peut-être quelqu'un S'il vous plaît aider? p>

classe: p> xxx pré>

form_form: p> xxx pré>

datagridview1.dataSource = Verticies; code> p>

code pour calculer lorsque vous appuyez sur la touche "(codé dur pour 4 points polygone - devrait être pour toute quantité ...) P>

        // 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);


4 commentaires

Une méthode typique que j'ai vue auparavant est de partitionner le polygone dans des triangles, vous pourriez simplement résumer la zone de tous les triangles. Ceci n'est toutefois pas séventiel car il a besoin d'algorithmes différents en fonction de la complexité des polygones (bords croisés, des trous, convexe / concave, etc.)


Vous pouvez envisager de poser cette question sur Mathoverflow.net , un site de trop-plein de pile, uniquement pour les questions mathématiques, assurez-vous simplement de poser la question de la question. une non-programmation et demandez plutôt l'approche algorithmique.


Mathoverflow est destiné aux mathématiciens professionnels qui souhaitent parler de problèmes dans les mathématiques de niveau post-diplômé.


Ok, pas étonnant que tout ressemblait à une langue étrangère pour moi :)


4 Réponses :


4
votes

Quelque chose comme ça pour un Polygone de l'avion (compilé avec bloc-notes):

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;
}


2 commentaires

Donc, il y a une différence entre la zone calculée en 2D et 3D?


Oui, juste un peu. La zone peut être calculée pour des objets unis uniquement. Par conséquent, votre polygone doit être simple - tous ses sommets doivent être situés dans la même plaine, sinon la zone ne peut pas être calculée. Le problème est que cet avion ne sera pas toujours Z = 0 plan comme dans votre exemple. Vous devez en tenir compte et traiter les points de manière appropriée avant de calculer la zone.



5
votes
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);
}

1 commentaires

Veuillez fournir une explication pour que votre réponse soit plus facile à comprendre.



21
votes

Utilisation d'expressions Lambda, cela devient trivial!

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);


2 commentaires

L'explication liée ne explique l'algorithme. Cela présente simplement le code différemment.


Bien que j'apprécie la succincence, je dois être partiellement d'accord avec @ cp.engr; probablement pas la solution la plus maintenable (lisible). Si je finis à faire quelque chose comme ça, j'espère que je posterai une alternative plus simple à comprendre



0
votes

Je l'ai constaté que lors du calcul de la zone d'environ 600 000 polygones, les formules de base ci-dessus a travaillé pour certains polygones, mais beaucoup ont été par un degré énorme. Je vérifiais place mes résultats contre https://geojson.io/ - qui a renvoyé des résultats corrects pour les polygones très complexes avec troués (par exemple lacs au milieu). Pour calculer la zone correcte pour un polygone complexe, je fini par utiliser le même système qui utilise geojson.io - bibliothèque Turf.js d'un côté client voir ici https://turfjs.org/docs/#area

Dans cette image, vous pouvez voir mon premier essai, puis mon deuxième en utilisant Turf.js - il y a une colonne, il montre un rapport de la façon correcte le premier essai a été comparée à la seconde, où 1 est le même calcul. Vous pouvez voir que la plupart du temps ils sont proches, mais certains sont par un facteur de 10 ou pire. P>

entrer image description ici p>

Voici un exemple de code pour effectuer le calcul. Je l'avais charger 200 sur l'écran, puis exécutez le calcul JS, et ajax le résultat à la base de données côté serveur. P>

{"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]]]}


0 commentaires